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