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