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