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