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