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