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