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