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