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