Deprecate uname() in favour of (Posix) gethostname().
[mmh] / sbr / mts.c
1
2 /*
3  * mts.c -- definitions for the mail transport system
4  *
5  * This code is Copyright (c) 2002, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 #include <h/mh.h>   /* for snprintf() */
11 #include <h/nmh.h>
12 #include <h/utils.h>
13
14 #define nmhetcdir(file) NMHETCDIR#file
15
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <h/mts.h>
19 #include <pwd.h>
20 #include <sys/socket.h>
21 #include <netdb.h>
22
23 #define NOTOK   (-1)
24 #define OK        0
25
26 /*
27  * static prototypes
28  */
29 static char *tailor_value (unsigned char *);
30 static void getuserinfo (void);
31 static const char *get_mtsconf_pathname(void);
32 static const char *get_mtsuserconf_pathname(void);
33 static void mts_read_conf_file (FILE *fp);
34
35 /*
36  * *mmdfldir and *uucpldir are the maildrop directories.  If maildrops
37  * are kept in the user's home directory, then these should be empty
38  * strings.  In this case, the appropriate ...lfil array should contain
39  * the name of the file in the user's home directory.  Usually, this is
40  * something like ".mail".
41  */
42
43 /*
44  * nmh mail transport interface customization file
45  */
46 static char *mtsconf = nmhetcdir(/mts.conf);
47
48 static char *localname   = "";
49 static char *localdomain = "";
50 static char *systemname  = "";
51
52 char *mmdfldir = MAILSPOOL;
53 char *mmdflfil = "";
54 char *uucpldir = "/usr/spool/mail";
55 char *uucplfil = "";
56
57 char *mmdlm1 = "\001\001\001\001\n";
58 char *mmdlm2 = "\001\001\001\001\n";
59
60 /* Cache the username and fullname of the user */
61 static char username[BUFSIZ];
62 static char fullname[BUFSIZ];
63
64 /* Variables for username masquerading: */
65        boolean  draft_from_masquerading = FALSE;  /* also used from post.c */
66 static boolean  mmailid_masquerading = FALSE;
67        boolean  username_extension_masquerading = FALSE;  /* " from addrsbr.c */
68 static char*    masquerade = "";
69
70 /*
71  * MTS specific variables
72  */
73 #if defined(SMTPMTS)
74 static char *sm_method = "smtp";
75 int  sm_mts    = MTS_SMTP;
76 char *hostable = nmhetcdir(/hosts);
77 char *sendmail = SENDMAILPATH;
78 #endif
79
80 /*
81  * SMTP/POP stuff
82  */
83 char *clientname = NULL;
84 char *servers    = "localhost \01localnet";
85 char *pophost    = "";
86
87 /*
88  * Global MailDelivery file
89  */
90 char *maildelivery = nmhetcdir(/maildelivery);
91
92
93 /*
94  * Aliasing Facility (doesn't belong here)
95  */
96 int Everyone = NOTOK;
97 static char *everyone = "-1";
98 char *NoShell = "";
99
100 /*
101  * Customize the MTS settings for nmh by adjusting
102  * the file mts.conf in the nmh etc directory.
103  */
104
105 struct bind {
106     char *keyword;
107     char **value;
108 };
109
110 static struct bind binds[] = {
111     { "localname", &localname },
112     { "localdomain", &localdomain },
113     { "systemname", &systemname },
114     { "mmdfldir", &mmdfldir },
115     { "mmdflfil", &mmdflfil },
116     { "uucpldir", &uucpldir },
117     { "uucplfil", &uucplfil },
118     { "mmdelim1", &mmdlm1 },
119     { "mmdelim2", &mmdlm2 },
120     { "masquerade", &masquerade },
121
122 #if defined(SMTPMTS)
123     { "mts",      &sm_method },
124     { "hostable", &hostable  },
125     { "sendmail", &sendmail  },
126 #endif
127
128     { "clientname",  &clientname },
129     { "servers", &servers },
130     { "pophost", &pophost },
131
132     { "maildelivery", &maildelivery },
133     { "everyone", &everyone },
134     { "noshell", &NoShell },
135     { NULL, NULL }
136 };
137
138
139 /*
140  * Read the configuration file for the nmh interface
141  * to the mail transport system (MTS).
142  */
143
144 void
145 mts_init (char *name)
146 {
147     const char *cp;
148     FILE *fp;
149     static int inited = 0;
150
151     if (inited++ || (fp = fopen (get_mtsconf_pathname(), "r")) == NULL)
152         return;
153     mts_read_conf_file(fp);
154     fclose (fp);
155
156     cp = get_mtsuserconf_pathname();
157     if (cp != NULL &&
158             ((fp = fopen (get_mtsuserconf_pathname(), "r")) != NULL)) {
159         mts_read_conf_file(fp);
160         fclose (fp);
161     }
162
163     Everyone = atoi (everyone);
164
165     if (strstr(masquerade, "draft_from") != NULL)
166         draft_from_masquerading = TRUE;
167
168     if (strstr(masquerade, "mmailid") != NULL)
169         mmailid_masquerading = TRUE;
170
171     if (strstr(masquerade, "username_extension") != NULL)
172         username_extension_masquerading = TRUE;
173
174 #ifdef SMTPMTS
175     if (strcmp(sm_method, "smtp") == 0)
176         sm_mts = MTS_SMTP;
177     else if (strcmp(sm_method, "sendmail") == 0)
178         sm_mts = MTS_SENDMAIL;
179     else {
180         advise(NULL, "unsupported \"mts\" value in mts.conf: %s", sm_method);
181         sm_mts = MTS_SMTP;
182     }
183 #endif
184 }
185
186
187 #define QUOTE   '\\'
188
189 /*
190  * Convert escaped values, malloc some new space,
191  * and copy string to malloc'ed memory.
192  */
193
194 static char *
195 tailor_value (unsigned char *s)
196 {
197     int i, r;
198     char *bp;
199     char buffer[BUFSIZ];
200     size_t len;
201
202     for (bp = buffer; *s; bp++, s++) {
203         if (*s != QUOTE) {
204             *bp = *s;
205         } else {
206             switch (*++s) {
207                 case 'b': *bp = '\b'; break;
208                 case 'f': *bp = '\f'; break;
209                 case 'n': *bp = '\n'; break;
210                 case 't': *bp = '\t'; break;
211
212                 case 0: s--;
213                 case QUOTE: 
214                     *bp = QUOTE;
215                     break;
216
217                 default: 
218                     if (!isdigit (*s)) {
219                         *bp++ = QUOTE;
220                         *bp = *s;
221                     }
222                     r = *s != '0' ? 10 : 8;
223                     for (i = 0; isdigit (*s); s++)
224                         i = i * r + *s - '0';
225                     s--;
226                     *bp = toascii (i);
227                     break;
228             }
229         }
230     }
231     *bp = 0;
232
233     len = strlen (buffer) + 1;
234     bp = mh_xmalloc (len);
235     memcpy (bp, buffer, len);
236
237     return bp;
238 }
239
240 /*
241  * Get the fully qualified name of the local host.
242  */
243
244 char *
245 LocalName (void)
246 {
247     static char buffer[BUFSIZ] = "";
248     struct addrinfo hints, *res;
249
250     /* check if we have cached the local name */
251     if (buffer[0])
252         return buffer;
253
254     mts_init ("mts");
255
256     /* check if the mts.conf file specifies a "localname" */
257     if (*localname) {
258         strncpy (buffer, localname, sizeof(buffer));
259     } else {
260         memset(buffer, 0, sizeof(buffer));
261         /* first get our local name */
262         gethostname (buffer, sizeof(buffer) - 1);
263         /* now fully qualify our name */
264
265         memset(&hints, 0, sizeof(hints));
266         hints.ai_flags = AI_CANONNAME;
267         hints.ai_family = PF_UNSPEC;
268         if (getaddrinfo(buffer, NULL, &hints, &res) == 0) {
269             strncpy(buffer, res->ai_canonname, sizeof(buffer) - 1);
270             freeaddrinfo(res);
271         }
272     }
273
274     /*
275      * If the mts.conf file specifies a "localdomain",
276      * we append that now.  This should rarely be needed.
277      */
278     if (*localdomain) {
279         strcat (buffer, ".");
280         strcat (buffer, localdomain);
281     }
282
283     return buffer;
284 }
285
286
287 /*
288  * This is only for UUCP mail.  It gets the hostname
289  * as part of the UUCP "domain".
290  */
291
292 char *
293 SystemName (void)
294 {
295     static char buffer[BUFSIZ] = "";
296
297     /* check if we have cached the system name */
298     if (buffer[0])
299         return buffer;
300
301     mts_init ("mts");
302
303     /* check if mts.conf file specifies a "systemname" */
304     if (*systemname) {
305         strncpy (buffer, systemname, sizeof(buffer));
306         return buffer;
307     }
308
309     gethostname (buffer, sizeof(buffer));
310
311     return buffer;
312 }
313
314
315 /*
316  * Get the username of current user
317  */
318
319 char *
320 getusername (void)
321 {
322     if (username[0] == '\0')
323         getuserinfo();
324
325     return username;
326 }
327
328
329 /*
330  * Get full name of current user (typically from GECOS
331  * field of password file).
332  */
333
334 char *
335 getfullname (void)
336 {
337     if (username[0] == '\0')
338         getuserinfo();
339
340     return fullname;
341 }
342
343
344 /*
345  * Find the user's username and full name, and cache them.
346  * Also, handle "mmailid" username masquerading controlled from the GECOS field
347  * of the passwd file. 
348  */
349
350 static void
351 getuserinfo (void)
352 {
353     register unsigned char *cp;
354     register char *np;
355     register struct passwd *pw;
356
357     if ((pw = getpwuid (getuid ())) == NULL
358             || pw->pw_name == NULL
359             || *pw->pw_name == '\0') {
360         strncpy (username, "unknown", sizeof(username));
361         snprintf (fullname, sizeof(fullname), "The Unknown User-ID (%d)",
362                 (int) getuid ());
363         return;
364     }
365
366     np = pw->pw_gecos;
367
368     /* Get the user's real name from the GECOS field.  Stop once we hit a ',',
369        which some OSes use to separate other 'finger' information in the GECOS
370        field, like phone number.  Also, if mmailid masquerading is turned on due
371        to "mmailid" appearing on the "masquerade:" line of mts.conf, stop if we
372        hit a '<' (which should precede any ','s). */
373 #ifndef BSD42
374     if (mmailid_masquerading)
375         /* Stop at ',' or '<'. */
376         for (cp = fullname; *np != '\0' && *np != ',' && *np != '<';
377              *cp++ = *np++)
378             continue;
379     else
380         /* Allow '<' as a legal character of the user's name.  This code is
381            basically a duplicate of the code above the "else" -- we don't
382            collapse it down to one copy and put the mmailid_masquerading check
383            inside the loop with "(x ? y : z)" because that's inefficient and the
384            value'll never change while it's in there. */
385         for (cp = fullname; *np != '\0' && *np != ',';
386              *cp++ = *np++)
387             continue;
388 #else /* BSD42 */
389     /* On BSD(-derived) systems, the system utilities that deal with the GECOS
390        field (finger, mail, sendmail, etc.) translate any '&' character in it to
391        the login name, with the first letter capitalized.  So, for instance,
392        fingering a user "bob" with the GECOS field "& Jones" would reveal him to
393        be "In real life: Bob Jones".  Surprisingly, though, the OS doesn't do
394        the translation for you, so we have to do it manually here. */
395     if (mmailid_masquerading)
396         /* Stop at ',' or '<'. */
397         for (cp = fullname;
398              *np != '\0' && *np != ',' && *np != '<';) {
399             if (*np == '&')     {       /* blech! */
400                 strcpy (cp, pw->pw_name);
401                 *cp = toupper(*cp);
402                 while (*cp)
403                     cp++;
404                 np++;
405             } else {
406                 *cp++ = *np++;
407             }
408         }
409     else
410         /* Allow '<' as a legal character of the user's name.  This code is
411            basically a duplicate of the code above the "else" -- we don't
412            collapse it down to one copy and put the mmailid_masquerading check
413            inside the loop with "(x ? y : z)" because that's inefficient and the
414            value'll never change while it's in there. */
415         for (cp = fullname;
416              *np != '\0' && *np != ',';) {
417             if (*np == '&')     {       /* blech! */
418                 strcpy (cp, pw->pw_name);
419                 *cp = toupper(*cp);
420                 while (*cp)
421                     cp++;
422                 np++;
423             } else {
424                 *cp++ = *np++;
425             }
426         }
427 #endif /* BSD42 */
428     *cp = '\0';
429
430     if (mmailid_masquerading) {
431         /* Do mmailid processing.  The GECOS field should have the form
432            "Full Name <fakeusername>".  For instance,
433            "Dan Harkless <Dan.Harkless>".  Naturally, you'll want your MTA to
434            have an alias (e.g. in /etc/aliases) from "fakeusername" to your
435            account name.  */ 
436         if (*np)
437             np++;
438         for (cp = username; *np && *np != '>'; *cp++ = *np++)
439             continue;
440         *cp = '\0';
441     }
442     if (!mmailid_masquerading || *np == '\0')
443         strncpy (username, pw->pw_name, sizeof(username));
444
445     /* The $SIGNATURE environment variable overrides the GECOS field's idea of
446        your real name. */
447     if ((cp = getenv ("SIGNATURE")) && *cp)
448         strncpy (fullname, cp, sizeof(fullname));
449
450     if (strchr(fullname, '.')) {                /*  quote any .'s */
451         char tmp[BUFSIZ];
452
453         /* should quote "'s too */
454         snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
455         strncpy (fullname, tmp, sizeof(fullname));
456     }
457
458     return;
459 }
460
461 static const char*
462 get_mtsconf_pathname (void)
463 {
464     const char *cp = getenv ( "MHMTSCONF ");
465     if (cp != NULL && *cp != '\0') {
466         return cp;
467     }
468     return mtsconf;
469 }
470
471 static const char*
472 get_mtsuserconf_pathname (void)
473 {
474     const char *cp = getenv ( "MHMTSUSERCONF" );
475     if (cp != NULL && *cp != '\0') {
476         return cp;
477     }
478     return NULL;
479 }
480
481 static void
482 mts_read_conf_file (FILE *fp)
483 {
484     unsigned char *bp;
485     char *cp, buffer[BUFSIZ];
486     struct bind *b;
487
488     while (fgets (buffer, sizeof(buffer), fp)) {
489         if (!(cp = strchr(buffer, '\n')))
490             break;
491         *cp = 0;
492         if (*buffer == '#' || *buffer == '\0')
493             continue;
494         if (!(bp = strchr(buffer, ':')))
495             break;
496         *bp++ = 0;
497         while (isspace (*bp))
498             *bp++ = 0;
499
500         for (b = binds; b->keyword; b++)
501             if (!strcmp (buffer, b->keyword))
502                 break;
503         if (b->keyword && (cp = tailor_value (bp)))
504             *b->value = cp;
505     }
506 }