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