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