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