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