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