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