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