Moved the sendmail path config line from mts.conf to the MH profile
[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 <netdb.h>
21
22 #ifdef HAVE_SYS_UTSNAME_H
23 # include <sys/utsname.h>
24 #endif
25
26 #define NOTOK   (-1)
27 #define OK        0
28
29 /*
30  * static prototypes
31  */
32 static char *tailor_value (unsigned char *);
33 static void getuserinfo (void);
34 static const char *get_mtsconf_pathname(void);
35 static const char *get_mtsuserconf_pathname(void);
36 static void mts_read_conf_file (FILE *fp);
37
38 /*
39  * *mmdfldir and *uucpldir are the maildrop directories.  If maildrops
40  * are kept in the user's home directory, then these should be empty
41  * strings.  In this case, the appropriate ...lfil array should contain
42  * the name of the file in the user's home directory.  Usually, this is
43  * something like ".mail".
44  */
45
46 /*
47  * nmh mail transport interface customization file
48  */
49 static char *mtsconf = nmhetcdir(/mts.conf);
50
51 char *mmdfldir = MAILSPOOL;
52 char *mmdflfil = "";
53 char *uucpldir = "/usr/spool/mail";
54 char *uucplfil = "";
55
56 char *mmdlm1 = "\001\001\001\001\n";
57 char *mmdlm2 = "\001\001\001\001\n";
58
59 /* Cache the username and fullname of the user */
60 static char username[BUFSIZ];
61 static char fullname[BUFSIZ];
62
63 /* Variables for username masquerading: */
64        boolean  draft_from_masquerading = FALSE;
65 static boolean  mmailid_masquerading = FALSE;
66        boolean  username_extension_masquerading = FALSE;  /* " from addrsbr.c */
67 static char*    masquerade = "";
68
69 /*
70  * Global MailDelivery file
71  */
72 char *maildelivery = nmhetcdir(/maildelivery);
73
74
75 /*
76  * Aliasing Facility (doesn't belong here)
77  */
78 int Everyone = NOTOK;
79 static char *everyone = "-1";
80 char *NoShell = "";
81
82 /*
83  * Customize the MTS settings for nmh by adjusting
84  * the file mts.conf in the nmh etc directory.
85  */
86
87 struct bind {
88     char *keyword;
89     char **value;
90 };
91
92 static struct bind binds[] = {
93     { "mmdfldir", &mmdfldir },
94     { "mmdflfil", &mmdflfil },
95     { "uucpldir", &uucpldir },
96     { "uucplfil", &uucplfil },
97     { "mmdelim1", &mmdlm1 },
98     { "mmdelim2", &mmdlm2 },
99     { "masquerade", &masquerade },
100     { "maildelivery", &maildelivery },
101     { "everyone", &everyone },
102     { "noshell", &NoShell },
103     { NULL, NULL }
104 };
105
106
107 /*
108  * Read the configuration file for the nmh interface
109  * to the mail transport system (MTS).
110  */
111
112 void
113 mts_init (char *name)
114 {
115     const char *cp;
116     FILE *fp;
117     static int inited = 0;
118
119     if (inited++ || (fp = fopen (get_mtsconf_pathname(), "r")) == NULL)
120         return;
121     mts_read_conf_file(fp);
122     fclose (fp);
123
124     cp = get_mtsuserconf_pathname();
125     if (cp != NULL &&
126             ((fp = fopen (get_mtsuserconf_pathname(), "r")) != NULL)) {
127         mts_read_conf_file(fp);
128         fclose (fp);
129     }
130
131     Everyone = atoi (everyone);
132
133     if (strstr(masquerade, "draft_from") != NULL)
134         draft_from_masquerading = TRUE;
135
136     if (strstr(masquerade, "mmailid") != NULL)
137         mmailid_masquerading = TRUE;
138
139     if (strstr(masquerade, "username_extension") != NULL)
140         username_extension_masquerading = TRUE;
141 }
142
143
144 #define QUOTE   '\\'
145
146 /*
147  * Convert escaped values, malloc some new space,
148  * and copy string to malloc'ed memory.
149  */
150
151 static char *
152 tailor_value (unsigned char *s)
153 {
154     int i, r;
155     char *bp;
156     char buffer[BUFSIZ];
157     size_t len;
158
159     for (bp = buffer; *s; bp++, s++) {
160         if (*s != QUOTE) {
161             *bp = *s;
162         } else {
163             switch (*++s) {
164                 case 'b': *bp = '\b'; break;
165                 case 'f': *bp = '\f'; break;
166                 case 'n': *bp = '\n'; break;
167                 case 't': *bp = '\t'; break;
168
169                 case 0: s--;
170                 case QUOTE: 
171                     *bp = QUOTE;
172                     break;
173
174                 default: 
175                     if (!isdigit (*s)) {
176                         *bp++ = QUOTE;
177                         *bp = *s;
178                     }
179                     r = *s != '0' ? 10 : 8;
180                     for (i = 0; isdigit (*s); s++)
181                         i = i * r + *s - '0';
182                     s--;
183                     *bp = toascii (i);
184                     break;
185             }
186         }
187     }
188     *bp = 0;
189
190     len = strlen (buffer) + 1;
191     bp = mh_xmalloc (len);
192     memcpy (bp, buffer, len);
193
194     return bp;
195 }
196
197 /*
198  * Get the fully qualified name of the local host.
199  */
200
201 char *
202 LocalName (void)
203 {
204     static char buffer[BUFSIZ] = "";
205     struct addrinfo hints, *res;
206 #ifdef HAVE_UNAME
207     struct utsname name;
208 #endif
209
210     /* check if we have cached the local name */
211     if (buffer[0])
212         return buffer;
213
214     mts_init ("mts");
215
216     memset(buffer, 0, sizeof(buffer));
217 #ifdef HAVE_UNAME
218     /* first get our local name */
219     uname (&name);
220     strncpy (buffer, name.nodename, sizeof(buffer) - 1);
221 #else
222     /* first get our local name */
223     gethostname (buffer, sizeof(buffer) - 1);
224 #endif
225     /* now fully qualify our name */
226
227     memset(&hints, 0, sizeof(hints));
228     hints.ai_flags = AI_CANONNAME;
229     hints.ai_family = PF_UNSPEC;
230     if (getaddrinfo(buffer, NULL, &hints, &res) == 0) {
231         strncpy(buffer, res->ai_canonname, sizeof(buffer) - 1);
232         freeaddrinfo(res);
233     }
234
235     return buffer;
236 }
237
238
239 /*
240  * This is only for UUCP mail.  It gets the hostname
241  * as part of the UUCP "domain".
242  */
243
244 char *
245 SystemName (void)
246 {
247     static char buffer[BUFSIZ] = "";
248
249 #ifdef HAVE_UNAME
250     struct utsname name;
251 #endif
252
253     /* check if we have cached the system name */
254     if (buffer[0])
255         return buffer;
256
257     mts_init ("mts");
258
259 #ifdef HAVE_UNAME
260     uname (&name);
261     strncpy (buffer, name.nodename, sizeof(buffer));
262 #else
263     gethostname (buffer, sizeof(buffer));
264 #endif
265
266     return buffer;
267 }
268
269
270 /*
271  * Get the username of current user
272  */
273
274 char *
275 getusername (void)
276 {
277     if (username[0] == '\0')
278         getuserinfo();
279
280     return username;
281 }
282
283
284 /*
285  * Get full name of current user (typically from GECOS
286  * field of password file).
287  */
288
289 char *
290 getfullname (void)
291 {
292     if (username[0] == '\0')
293         getuserinfo();
294
295     return fullname;
296 }
297
298
299 /*
300  * Find the user's username and full name, and cache them.
301  * Also, handle "mmailid" username masquerading controlled from the GECOS field
302  * of the passwd file. 
303  */
304
305 static void
306 getuserinfo (void)
307 {
308     register unsigned char *cp;
309     register char *np;
310     register struct passwd *pw;
311
312     if ((pw = getpwuid (getuid ())) == NULL
313             || pw->pw_name == NULL
314             || *pw->pw_name == '\0') {
315         strncpy (username, "unknown", sizeof(username));
316         snprintf (fullname, sizeof(fullname), "The Unknown User-ID (%d)",
317                 (int) getuid ());
318         return;
319     }
320
321     np = pw->pw_gecos;
322
323     /* Get the user's real name from the GECOS field.  Stop once we hit a ',',
324        which some OSes use to separate other 'finger' information in the GECOS
325        field, like phone number.  Also, if mmailid masquerading is turned on due
326        to "mmailid" appearing on the "masquerade:" line of mts.conf, stop if we
327        hit a '<' (which should precede any ','s). */
328 #ifndef BSD42
329     if (mmailid_masquerading)
330         /* Stop at ',' or '<'. */
331         for (cp = fullname; *np != '\0' && *np != ',' && *np != '<';
332              *cp++ = *np++)
333             continue;
334     else
335         /* Allow '<' as a legal character of the user's name.  This code is
336            basically a duplicate of the code above the "else" -- we don't
337            collapse it down to one copy and put the mmailid_masquerading check
338            inside the loop with "(x ? y : z)" because that's inefficient and the
339            value'll never change while it's in there. */
340         for (cp = fullname; *np != '\0' && *np != ',';
341              *cp++ = *np++)
342             continue;
343 #else /* BSD42 */
344     /* On BSD(-derived) systems, the system utilities that deal with the GECOS
345        field (finger, mail, sendmail, etc.) translate any '&' character in it to
346        the login name, with the first letter capitalized.  So, for instance,
347        fingering a user "bob" with the GECOS field "& Jones" would reveal him to
348        be "In real life: Bob Jones".  Surprisingly, though, the OS doesn't do
349        the translation for you, so we have to do it manually here. */
350     if (mmailid_masquerading)
351         /* Stop at ',' or '<'. */
352         for (cp = fullname;
353              *np != '\0' && *np != ',' && *np != '<';) {
354             if (*np == '&')     {       /* blech! */
355                 strcpy (cp, pw->pw_name);
356                 *cp = toupper(*cp);
357                 while (*cp)
358                     cp++;
359                 np++;
360             } else {
361                 *cp++ = *np++;
362             }
363         }
364     else
365         /* Allow '<' as a legal character of the user's name.  This code is
366            basically a duplicate of the code above the "else" -- we don't
367            collapse it down to one copy and put the mmailid_masquerading check
368            inside the loop with "(x ? y : z)" because that's inefficient and the
369            value'll never change while it's in there. */
370         for (cp = fullname;
371              *np != '\0' && *np != ',';) {
372             if (*np == '&')     {       /* blech! */
373                 strcpy (cp, pw->pw_name);
374                 *cp = toupper(*cp);
375                 while (*cp)
376                     cp++;
377                 np++;
378             } else {
379                 *cp++ = *np++;
380             }
381         }
382 #endif /* BSD42 */
383     *cp = '\0';
384
385     if (mmailid_masquerading) {
386         /* Do mmailid processing.  The GECOS field should have the form
387            "Full Name <fakeusername>".  For instance,
388            "Dan Harkless <Dan.Harkless>".  Naturally, you'll want your MTA to
389            have an alias (e.g. in /etc/aliases) from "fakeusername" to your
390            account name.  */ 
391         if (*np)
392             np++;
393         for (cp = username; *np && *np != '>'; *cp++ = *np++)
394             continue;
395         *cp = '\0';
396     }
397     if (!mmailid_masquerading || *np == '\0')
398         strncpy (username, pw->pw_name, sizeof(username));
399
400     /* The $SIGNATURE environment variable overrides the GECOS field's idea of
401        your real name. */
402     if ((cp = getenv ("SIGNATURE")) && *cp)
403         strncpy (fullname, cp, sizeof(fullname));
404
405     if (strchr(fullname, '.')) {                /*  quote any .'s */
406         char tmp[BUFSIZ];
407
408         /* should quote "'s too */
409         snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
410         strncpy (fullname, tmp, sizeof(fullname));
411     }
412
413     return;
414 }
415
416 static const char*
417 get_mtsconf_pathname (void)
418 {
419     const char *cp = getenv ( "MHMTSCONF ");
420     if (cp != NULL && *cp != '\0') {
421         return cp;
422     }
423     return mtsconf;
424 }
425
426 static const char*
427 get_mtsuserconf_pathname (void)
428 {
429     const char *cp = getenv ( "MHMTSUSERCONF" );
430     if (cp != NULL && *cp != '\0') {
431         return cp;
432     }
433     return NULL;
434 }
435
436 static void
437 mts_read_conf_file (FILE *fp)
438 {
439     unsigned char *bp;
440     char *cp, buffer[BUFSIZ];
441     struct bind *b;
442
443     while (fgets (buffer, sizeof(buffer), fp)) {
444         if (!(cp = strchr(buffer, '\n')))
445             break;
446         *cp = 0;
447         if (*buffer == '#' || *buffer == '\0')
448             continue;
449         if (!(bp = strchr(buffer, ':')))
450             break;
451         *bp++ = 0;
452         while (isspace (*bp))
453             *bp++ = 0;
454
455         for (b = binds; b->keyword; b++)
456             if (!strcmp (buffer, b->keyword))
457                 break;
458         if (b->keyword && (cp = tailor_value (bp)))
459             *b->value = cp;
460     }
461 }