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