ba7ff0757ed3d0abe0fc65da359f6445963ccc9c
[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 <sys/socket.h>
21 #include <netdb.h>
22
23 /*
24  * static prototypes
25  */
26 static char *tailor_value (unsigned char *);
27 static void getuserinfo (void);
28 static const char *get_mtsconf_pathname(void);
29 static const char *get_mtsuserconf_pathname(void);
30 static void mts_read_conf_file (FILE *fp);
31
32 /*
33  * *mmdfldir and *uucpldir are the maildrop directories.  If maildrops
34  * are kept in the user's home directory, then these should be empty
35  * strings.  In this case, the appropriate ...lfil array should contain
36  * the name of the file in the user's home directory.  Usually, this is
37  * something like ".mail".
38  */
39
40 /*
41  * nmh mail transport interface customization file
42  */
43 static char *mtsconf = nmhetcdir(/mts.conf);
44
45 static char *localname   = "";
46 static char *localdomain = "";
47 static char *systemname  = "";
48
49 char *mmdfldir = MAILSPOOL;
50 char *mmdflfil = "";
51 char *uucpldir = "/usr/spool/mail";
52 char *uucplfil = "";
53
54 char *mmdlm1 = "\001\001\001\001\n";
55 char *mmdlm2 = "\001\001\001\001\n";
56
57 /* Cache the username, fullname, and mailbox of the user */
58 static char username[BUFSIZ];
59 static char fullname[BUFSIZ];
60 static char localmbox[BUFSIZ];
61
62 /* Variables for username masquerading: */
63        boolean  draft_from_masquerading = FALSE;  /* also used from post.c */
64 static boolean  mmailid_masquerading = FALSE;
65        boolean  username_extension_masquerading = FALSE;  /* " from addrsbr.c */
66 static char*    masquerade = "";
67
68 /*
69  * MTS specific variables
70  */
71 static char *sm_method = "smtp";
72 int  sm_mts    = MTS_SMTP;
73 char *hostable = nmhetcdir(/hosts);
74 char *sendmail = SENDMAILPATH;
75
76 /*
77  * SMTP/POP stuff
78  */
79 char *clientname = NULL;
80 char *servers    = "localhost";
81 char *pophost    = "";
82
83 /*
84  * Global MailDelivery file
85  */
86 char *maildelivery = nmhetcdir(/maildelivery);
87
88
89 /*
90  * Aliasing Facility (doesn't belong here)
91  */
92 int Everyone = NOTOK;
93 static char *everyone = "-1";
94 char *NoShell = "";
95
96 /*
97  * Customize the MTS settings for nmh by adjusting
98  * the file mts.conf in the nmh etc directory.
99  */
100
101 struct bind {
102     char *keyword;
103     char **value;
104 };
105
106 static struct bind binds[] = {
107     { "localname", &localname },
108     { "localdomain", &localdomain },
109     { "systemname", &systemname },
110     { "mmdfldir", &mmdfldir },
111     { "mmdflfil", &mmdflfil },
112     { "uucpldir", &uucpldir },
113     { "uucplfil", &uucplfil },
114     { "mmdelim1", &mmdlm1 },
115     { "mmdelim2", &mmdlm2 },
116     { "masquerade", &masquerade },
117     { "mts",      &sm_method },
118     { "hostable", &hostable  },
119     { "sendmail", &sendmail  },
120     { "clientname",  &clientname },
121     { "servers", &servers },
122     { "pophost", &pophost },
123
124     { "maildelivery", &maildelivery },
125     { "everyone", &everyone },
126     { "noshell", &NoShell },
127     { NULL, NULL }
128 };
129
130
131 /*
132  * Read the configuration file for the nmh interface
133  * to the mail transport system (MTS).
134  */
135
136 void
137 mts_init (char *name)
138 {
139     NMH_UNUSED (name);
140
141     const char *cp;
142     FILE *fp;
143     static int inited = 0;
144
145     if (inited++ || (fp = fopen (get_mtsconf_pathname(), "r")) == NULL)
146         return;
147     mts_read_conf_file(fp);
148     fclose (fp);
149
150     cp = get_mtsuserconf_pathname();
151     if (cp != NULL &&
152             ((fp = fopen (get_mtsuserconf_pathname(), "r")) != NULL)) {
153         mts_read_conf_file(fp);
154         fclose (fp);
155     }
156
157     Everyone = atoi (everyone);
158
159     if (strstr(masquerade, "draft_from") != NULL)
160         draft_from_masquerading = TRUE;
161
162     if (strstr(masquerade, "mmailid") != NULL)
163         mmailid_masquerading = TRUE;
164
165     if (strstr(masquerade, "username_extension") != NULL)
166         username_extension_masquerading = TRUE;
167
168     if (strcmp(sm_method, "smtp") == 0)
169         sm_mts = MTS_SMTP;
170     else if (strcmp(sm_method, "sendmail") == 0)
171         sm_mts = MTS_SENDMAIL;
172     else {
173         advise(NULL, "unsupported \"mts\" value in mts.conf: %s", sm_method);
174         sm_mts = MTS_SMTP;
175     }
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  * If flag is 0, then use anything out of mts.conf (like localname).
236  * If flag is 1, then only use the "proper" local hostname.
237  */
238
239 char *
240 LocalName (int flag)
241 {
242     static char buffer0[BUFSIZ] = "";
243     static char buffer1[BUFSIZ] = "";
244     static char *buffer[] = { buffer0, buffer1 };
245     char *buf;
246     struct addrinfo hints, *res;
247
248     if (flag < 0 || flag > 1)
249         return NULL;
250
251     buf = buffer[flag];
252
253     /* check if we have cached the local name */
254     if (buf[0])
255         return buf;
256
257     mts_init ("mts");
258
259     /* check if the mts.conf file specifies a "localname" */
260     if (*localname && flag == 0) {
261         strncpy (buf, localname, sizeof(buffer0));
262     } else {
263         memset(buf, 0, sizeof(buffer0));
264         /* first get our local name */
265         gethostname (buf, sizeof(buffer0) - 1);
266         /* now fully qualify our name */
267
268         memset(&hints, 0, sizeof(hints));
269         hints.ai_flags = AI_CANONNAME;
270         hints.ai_family = PF_UNSPEC;
271         if (getaddrinfo(buf, NULL, &hints, &res) == 0) {
272             strncpy(buf, res->ai_canonname, sizeof(buffer0) - 1);
273             freeaddrinfo(res);
274         }
275     }
276
277     /*
278      * If the mts.conf file specifies a "localdomain",
279      * we append that now.  This should rarely be needed.
280      */
281     if (*localdomain) {
282         strcat (buf, ".");
283         strcat (buf, localdomain);
284     }
285
286     return buf;
287 }
288
289
290 /*
291  * This is only for UUCP mail.  It gets the hostname
292  * as part of the UUCP "domain".
293  */
294
295 char *
296 SystemName (void)
297 {
298     static char buffer[BUFSIZ] = "";
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     gethostname (buffer, sizeof(buffer));
313
314     return buffer;
315 }
316
317
318 /*
319  * Get the username of current user
320  */
321
322 char *
323 getusername (void)
324 {
325     if (username[0] == '\0')
326         getuserinfo();
327
328     return username;
329 }
330
331
332 /*
333  * Get full name of current user (typically from GECOS
334  * field of password file).
335  */
336
337 char *
338 getfullname (void)
339 {
340     if (username[0] == '\0')
341         getuserinfo();
342
343     return fullname;
344 }
345
346
347 /*
348  * Get the full local mailbox name.  This is in the form:
349  *
350  * User Name <user@name.com>
351  */
352
353 char *
354 getlocalmbox (void)
355 {
356     if (username[0] == '\0')
357         getuserinfo();
358
359     if (localmbox[0] == '\0') {
360         char *cp;
361
362         if ((cp = context_find("Local-Mailbox")) != NULL) {
363             strncpy(localmbox, cp, sizeof(localmbox));
364         } else {
365             snprintf(localmbox, sizeof(localmbox), "%s <%s@%s>", fullname,
366                      username, LocalName(0));
367         }
368
369         localmbox[sizeof(localmbox) - 1] = '\0';
370     }
371
372     return localmbox;
373 }
374
375 /*
376  * Find the user's username and full name, and cache them.
377  * Also, handle "mmailid" username masquerading controlled from the GECOS field
378  * of the passwd file. 
379  */
380
381 static void
382 getuserinfo (void)
383 {
384     register unsigned char *cp;
385     register char *np;
386     register struct passwd *pw;
387
388     if ((pw = getpwuid (getuid ())) == NULL
389             || pw->pw_name == NULL
390             || *pw->pw_name == '\0') {
391         strncpy (username, "unknown", sizeof(username));
392         snprintf (fullname, sizeof(fullname), "The Unknown User-ID (%d)",
393                 (int) getuid ());
394         return;
395     }
396
397     np = pw->pw_gecos;
398
399     /* Get the user's real name from the GECOS field.  Stop once we hit a ',',
400        which some OSes use to separate other 'finger' information in the GECOS
401        field, like phone number.  Also, if mmailid masquerading is turned on due
402        to "mmailid" appearing on the "masquerade:" line of mts.conf, stop if we
403        hit a '<' (which should precede any ','s). */
404     if (mmailid_masquerading)
405         /* Stop at ',' or '<'. */
406         for (cp = fullname; *np != '\0' && *np != ',' && *np != '<';
407              *cp++ = *np++)
408             continue;
409     else
410         /* Allow '<' as a legal character of the user's name.  This code is
411            basically a duplicate of the code above the "else" -- we don't
412            collapse it down to one copy and put the mmailid_masquerading check
413            inside the loop with "(x ? y : z)" because that's inefficient and the
414            value'll never change while it's in there. */
415         for (cp = fullname; *np != '\0' && *np != ',';
416              *cp++ = *np++)
417             continue;
418     *cp = '\0';
419
420     if (mmailid_masquerading) {
421         /* Do mmailid processing.  The GECOS field should have the form
422            "Full Name <fakeusername>".  For instance,
423            "Dan Harkless <Dan.Harkless>".  Naturally, you'll want your MTA to
424            have an alias (e.g. in /etc/aliases) from "fakeusername" to your
425            account name.  */ 
426         if (*np)
427             np++;
428         for (cp = username; *np && *np != '>'; *cp++ = *np++)
429             continue;
430         *cp = '\0';
431     }
432     if (!mmailid_masquerading || *np == '\0')
433         strncpy (username, pw->pw_name, sizeof(username));
434
435     /* The $SIGNATURE environment variable overrides the GECOS field's idea of
436        your real name. If SIGNATURE isn't set, use the Signature profile
437        setting if it exists. */
438     if ((cp = getenv ("SIGNATURE")) && *cp)
439         strncpy (fullname, cp, sizeof(fullname));
440     else if ((cp = context_find("Signature")))
441         strncpy (fullname, cp, sizeof(fullname));
442
443     if (strchr(fullname, '.')) {                /*  quote any .'s */
444         char tmp[BUFSIZ];
445
446         /* should quote "'s too */
447         snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
448         strncpy (fullname, tmp, sizeof(fullname));
449     }
450
451     fullname[sizeof(fullname) - 1] = '\0';
452
453     localmbox[0] = '\0';
454
455     return;
456 }
457
458 static const char*
459 get_mtsconf_pathname (void)
460 {
461     const char *cp = getenv ( "MHMTSCONF" );
462     if (cp != NULL && *cp != '\0') {
463         return cp;
464     }
465     return mtsconf;
466 }
467
468 static const char*
469 get_mtsuserconf_pathname (void)
470 {
471     const char *cp = getenv ( "MHMTSUSERCONF" );
472     if (cp != NULL && *cp != '\0') {
473         return cp;
474     }
475     return NULL;
476 }
477
478 static void
479 mts_read_conf_file (FILE *fp)
480 {
481     unsigned char *bp;
482     char *cp, buffer[BUFSIZ];
483     struct bind *b;
484
485     while (fgets (buffer, sizeof(buffer), fp)) {
486         if (!(cp = strchr(buffer, '\n')))
487             break;
488         *cp = 0;
489         if (*buffer == '#' || *buffer == '\0')
490             continue;
491         if (!(bp = strchr(buffer, ':')))
492             break;
493         *bp++ = 0;
494         while (isspace (*bp))
495             *bp++ = 0;
496
497         for (b = binds; b->keyword; b++)
498             if (!strcmp (buffer, b->keyword))
499                 break;
500         if (b->keyword && (cp = tailor_value (bp)))
501             *b->value = cp;
502     }
503 }