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