939a8c7685ee311ee3e57e1a610694918556be14
[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 and fullname of the user */
58 static char username[BUFSIZ];
59 static char fullname[BUFSIZ];
60
61 /* Variables for username masquerading: */
62        boolean  draft_from_masquerading = FALSE;  /* also used from post.c */
63 static boolean  mmailid_masquerading = FALSE;
64        boolean  username_extension_masquerading = FALSE;  /* " from addrsbr.c */
65 static char*    masquerade = "";
66
67 /*
68  * MTS specific variables
69  */
70 #if defined(SMTPMTS)
71 static char *sm_method = "smtp";
72 int  sm_mts    = MTS_SMTP;
73 char *hostable = nmhetcdir(/hosts);
74 char *sendmail = SENDMAILPATH;
75 #endif
76
77 /*
78  * SMTP/POP stuff
79  */
80 char *clientname = NULL;
81 char *servers    = "localhost";
82 char *pophost    = "";
83
84 /*
85  * Global MailDelivery file
86  */
87 char *maildelivery = nmhetcdir(/maildelivery);
88
89
90 /*
91  * Aliasing Facility (doesn't belong here)
92  */
93 int Everyone = NOTOK;
94 static char *everyone = "-1";
95 char *NoShell = "";
96
97 /*
98  * Customize the MTS settings for nmh by adjusting
99  * the file mts.conf in the nmh etc directory.
100  */
101
102 struct bind {
103     char *keyword;
104     char **value;
105 };
106
107 static struct bind binds[] = {
108     { "localname", &localname },
109     { "localdomain", &localdomain },
110     { "systemname", &systemname },
111     { "mmdfldir", &mmdfldir },
112     { "mmdflfil", &mmdflfil },
113     { "uucpldir", &uucpldir },
114     { "uucplfil", &uucplfil },
115     { "mmdelim1", &mmdlm1 },
116     { "mmdelim2", &mmdlm2 },
117     { "masquerade", &masquerade },
118
119 #if defined(SMTPMTS)
120     { "mts",      &sm_method },
121     { "hostable", &hostable  },
122     { "sendmail", &sendmail  },
123 #endif
124
125     { "clientname",  &clientname },
126     { "servers", &servers },
127     { "pophost", &pophost },
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     NMH_UNUSED (name);
145
146     const char *cp;
147     FILE *fp;
148     static int inited = 0;
149
150     if (inited++ || (fp = fopen (get_mtsconf_pathname(), "r")) == NULL)
151         return;
152     mts_read_conf_file(fp);
153     fclose (fp);
154
155     cp = get_mtsuserconf_pathname();
156     if (cp != NULL &&
157             ((fp = fopen (get_mtsuserconf_pathname(), "r")) != NULL)) {
158         mts_read_conf_file(fp);
159         fclose (fp);
160     }
161
162     Everyone = atoi (everyone);
163
164     if (strstr(masquerade, "draft_from") != NULL)
165         draft_from_masquerading = TRUE;
166
167     if (strstr(masquerade, "mmailid") != NULL)
168         mmailid_masquerading = TRUE;
169
170     if (strstr(masquerade, "username_extension") != NULL)
171         username_extension_masquerading = TRUE;
172
173 #ifdef SMTPMTS
174     if (strcmp(sm_method, "smtp") == 0)
175         sm_mts = MTS_SMTP;
176     else if (strcmp(sm_method, "sendmail") == 0)
177         sm_mts = MTS_SENDMAIL;
178     else {
179         advise(NULL, "unsupported \"mts\" value in mts.conf: %s", sm_method);
180         sm_mts = MTS_SMTP;
181     }
182 #endif
183 }
184
185
186 #define QUOTE   '\\'
187
188 /*
189  * Convert escaped values, malloc some new space,
190  * and copy string to malloc'ed memory.
191  */
192
193 static char *
194 tailor_value (unsigned char *s)
195 {
196     int i, r;
197     char *bp;
198     char buffer[BUFSIZ];
199     size_t len;
200
201     for (bp = buffer; *s; bp++, s++) {
202         if (*s != QUOTE) {
203             *bp = *s;
204         } else {
205             switch (*++s) {
206                 case 'b': *bp = '\b'; break;
207                 case 'f': *bp = '\f'; break;
208                 case 'n': *bp = '\n'; break;
209                 case 't': *bp = '\t'; break;
210
211                 case 0: s--;
212                 case QUOTE: 
213                     *bp = QUOTE;
214                     break;
215
216                 default: 
217                     if (!isdigit (*s)) {
218                         *bp++ = QUOTE;
219                         *bp = *s;
220                     }
221                     r = *s != '0' ? 10 : 8;
222                     for (i = 0; isdigit (*s); s++)
223                         i = i * r + *s - '0';
224                     s--;
225                     *bp = toascii (i);
226                     break;
227             }
228         }
229     }
230     *bp = 0;
231
232     len = strlen (buffer) + 1;
233     bp = mh_xmalloc (len);
234     memcpy (bp, buffer, len);
235
236     return bp;
237 }
238
239 /*
240  * Get the fully qualified name of the local host.
241  *
242  * If flag is 0, then use anything out of mts.conf (like localname).
243  * If flag is 1, then only use the "proper" local hostname.
244  */
245
246 char *
247 LocalName (int flag)
248 {
249     static char buffer0[BUFSIZ] = "";
250     static char buffer1[BUFSIZ] = "";
251     static char *buffer[] = { buffer0, buffer1 };
252     char *buf;
253     struct addrinfo hints, *res;
254
255     if (flag < 0 || flag > 1)
256         return NULL;
257
258     buf = buffer[flag];
259
260     /* check if we have cached the local name */
261     if (buf[0])
262         return buf;
263
264     mts_init ("mts");
265
266     /* check if the mts.conf file specifies a "localname" */
267     if (*localname && flag == 0) {
268         strncpy (buf, localname, sizeof(buffer0));
269     } else {
270         memset(buf, 0, sizeof(buffer0));
271         /* first get our local name */
272         gethostname (buf, sizeof(buffer0) - 1);
273         /* now fully qualify our name */
274
275         memset(&hints, 0, sizeof(hints));
276         hints.ai_flags = AI_CANONNAME;
277         hints.ai_family = PF_UNSPEC;
278         if (getaddrinfo(buf, NULL, &hints, &res) == 0) {
279             strncpy(buf, res->ai_canonname, sizeof(buffer0) - 1);
280             freeaddrinfo(res);
281         }
282     }
283
284     /*
285      * If the mts.conf file specifies a "localdomain",
286      * we append that now.  This should rarely be needed.
287      */
288     if (*localdomain) {
289         strcat (buf, ".");
290         strcat (buf, localdomain);
291     }
292
293     return buf;
294 }
295
296
297 /*
298  * This is only for UUCP mail.  It gets the hostname
299  * as part of the UUCP "domain".
300  */
301
302 char *
303 SystemName (void)
304 {
305     static char buffer[BUFSIZ] = "";
306
307     /* check if we have cached the system name */
308     if (buffer[0])
309         return buffer;
310
311     mts_init ("mts");
312
313     /* check if mts.conf file specifies a "systemname" */
314     if (*systemname) {
315         strncpy (buffer, systemname, sizeof(buffer));
316         return buffer;
317     }
318
319     gethostname (buffer, sizeof(buffer));
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     if (mmailid_masquerading)
384         /* Stop at ',' or '<'. */
385         for (cp = fullname; *np != '\0' && *np != ',' && *np != '<';
386              *cp++ = *np++)
387             continue;
388     else
389         /* Allow '<' as a legal character of the user's name.  This code is
390            basically a duplicate of the code above the "else" -- we don't
391            collapse it down to one copy and put the mmailid_masquerading check
392            inside the loop with "(x ? y : z)" because that's inefficient and the
393            value'll never change while it's in there. */
394         for (cp = fullname; *np != '\0' && *np != ',';
395              *cp++ = *np++)
396             continue;
397     *cp = '\0';
398
399     if (mmailid_masquerading) {
400         /* Do mmailid processing.  The GECOS field should have the form
401            "Full Name <fakeusername>".  For instance,
402            "Dan Harkless <Dan.Harkless>".  Naturally, you'll want your MTA to
403            have an alias (e.g. in /etc/aliases) from "fakeusername" to your
404            account name.  */ 
405         if (*np)
406             np++;
407         for (cp = username; *np && *np != '>'; *cp++ = *np++)
408             continue;
409         *cp = '\0';
410     }
411     if (!mmailid_masquerading || *np == '\0')
412         strncpy (username, pw->pw_name, sizeof(username));
413
414     /* The $SIGNATURE environment variable overrides the GECOS field's idea of
415        your real name. */
416     if ((cp = getenv ("SIGNATURE")) && *cp)
417         strncpy (fullname, cp, sizeof(fullname));
418
419     if (strchr(fullname, '.')) {                /*  quote any .'s */
420         char tmp[BUFSIZ];
421
422         /* should quote "'s too */
423         snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
424         strncpy (fullname, tmp, sizeof(fullname));
425     }
426
427     return;
428 }
429
430 static const char*
431 get_mtsconf_pathname (void)
432 {
433     const char *cp = getenv ( "MHMTSCONF ");
434     if (cp != NULL && *cp != '\0') {
435         return cp;
436     }
437     return mtsconf;
438 }
439
440 static const char*
441 get_mtsuserconf_pathname (void)
442 {
443     const char *cp = getenv ( "MHMTSUSERCONF" );
444     if (cp != NULL && *cp != '\0') {
445         return cp;
446     }
447     return NULL;
448 }
449
450 static void
451 mts_read_conf_file (FILE *fp)
452 {
453     unsigned char *bp;
454     char *cp, buffer[BUFSIZ];
455     struct bind *b;
456
457     while (fgets (buffer, sizeof(buffer), fp)) {
458         if (!(cp = strchr(buffer, '\n')))
459             break;
460         *cp = 0;
461         if (*buffer == '#' || *buffer == '\0')
462             continue;
463         if (!(bp = strchr(buffer, ':')))
464             break;
465         *bp++ = 0;
466         while (isspace (*bp))
467             *bp++ = 0;
468
469         for (b = binds; b->keyword; b++)
470             if (!strcmp (buffer, b->keyword))
471                 break;
472         if (b->keyword && (cp = tailor_value (bp)))
473             *b->value = cp;
474     }
475 }