1 /* mts.c -- definitions for the mail transport system
2  *
3  * This code is Copyright (c) 2002, by the authors of nmh.  See the
4  * COPYRIGHT file in the root directory of the nmh distribution for
5  * complete copyright information.
6  */
7 
8 #include <h/mh.h>   /* for snprintf() */
9 #include <h/utils.h>
10 
11 #define nmhetcdir(file) NMHETCDIR#file
12 
13 #include <h/mts.h>
14 #include <pwd.h>
15 #include <sys/socket.h>
16 #include <netdb.h>
17 
18 /*
19  * static prototypes
20  */
21 static char *tailor_value (char *);
22 static void getuserinfo (void);
23 static const char *get_mtsconf_pathname(void);
24 static const char *get_mtsuserconf_pathname(void);
25 static void mts_read_conf_file (FILE *fp);
26 
27 /*
28  * *mmdfldir and *uucpldir are the maildrop directories.  If maildrops
29  * are kept in the user's home directory, then these should be empty
30  * strings.  In this case, the appropriate ...lfil array should contain
31  * the name of the file in the user's home directory.  Usually, this is
32  * something like ".mail".
33  */
34 
35 /*
36  * nmh mail transport interface customization file
37  */
38 static char *mtsconf = nmhetcdir(/mts.conf);
39 
40 static char *localname   = "";
41 static char *localdomain = "";
42 static char *systemname  = "";
43 
44 char *mmdfldir = MAILSPOOL;
45 char *mmdflfil = "";
46 char *uucpldir = "/usr/spool/mail";
47 char *uucplfil = "";
48 
49 char *spoollocking = DEFAULT_LOCKING;
50 
51 /* Cache the username, fullname, and mailbox of the user */
52 static char username[BUFSIZ];
53 static char fullname[BUFSIZ];
54 static char localmbox[BUFSIZ];
55 
56 /*
57  * MTS specific variables
58  */
59 static char *mts_method = "smtp";
60 int  sm_mts    = MTS_SMTP;
61 char *sendmail = SENDMAILPATH;
62 
63 /*
64  * SMTP/POP stuff
65  */
66 char *clientname = NULL;
67 char *servers    = "localhost";
68 char *pophost    = "";
69 
70 /*
71  * Global MailDelivery file
72  */
73 char *maildelivery = nmhetcdir(/maildelivery);
74 
75 
76 /*
77  * Customize the MTS settings for nmh by adjusting
78  * the file mts.conf in the nmh etc directory.
79  */
80 
81 struct bind {
82     char *keyword;
83     char **value;
84 };
85 
86 static struct bind binds[] = {
87     { "localname", &localname },
88     { "localdomain", &localdomain },
89     { "systemname", &systemname },
90     { "mmdfldir", &mmdfldir },
91     { "mmdflfil", &mmdflfil },
92     { "spoollocking", &spoollocking },
93     { "uucpldir", &uucpldir },
94     { "uucplfil", &uucplfil },
95     { "mts",      &mts_method },
96     { "sendmail", &sendmail  },
97     { "clientname",  &clientname },
98     { "servers", &servers },
99     { "pophost", &pophost },
100 
101     { "maildelivery", &maildelivery },
102     { NULL, NULL }
103 };
104 
105 
106 /* Convert name of mts method to integer value and store it. */
107 void
save_mts_method(const char * value)108 save_mts_method (const char *value) {
109     if (! strcasecmp (value, "smtp")) {
110         mts_method = "smtp";
111         sm_mts = MTS_SMTP;
112     } else if (! strcasecmp (value, "sendmail/smtp")  ||
113                ! strcasecmp (value, "sendmail")) {
114         mts_method = "sendmail/smtp";
115         sm_mts = MTS_SENDMAIL_SMTP;
116     } else if (! strcasecmp (value, "sendmail/pipe")) {
117         mts_method = "sendmail/pipe";
118         sm_mts = MTS_SENDMAIL_PIPE;
119     } else {
120         adios (NULL, "unsupported mts selection \"%s\"", value);
121     }
122 }
123 
124 
125 /*
126  * Read the configuration file for the nmh interface
127  * to the mail transport system (MTS).
128  */
129 
130 void
mts_init(void)131 mts_init (void)
132 {
133     const char *cp;
134     FILE *fp;
135     static int inited = 0;
136 
137     if (inited++ || (fp = fopen (get_mtsconf_pathname(), "r")) == NULL)
138 	return;
139     mts_read_conf_file(fp);
140     fclose (fp);
141 
142     cp = get_mtsuserconf_pathname();
143     if (cp != NULL &&
144             ((fp = fopen (get_mtsuserconf_pathname(), "r")) != NULL)) {
145         mts_read_conf_file(fp);
146         fclose (fp);
147     }
148 
149     save_mts_method (mts_method);
150 }
151 
152 
153 #define	QUOTE	'\\'
154 
155 /*
156  * Convert escaped values, malloc some new space,
157  * and copy string to malloc'ed memory.
158  */
159 
160 static char *
tailor_value(char * s)161 tailor_value (char *s)
162 {
163     int i, r;
164     char *bp;
165     char buffer[BUFSIZ];
166 
167     for (bp = buffer; *s; bp++, s++) {
168 	if (*s != QUOTE) {
169 	    *bp = *s;
170 	} else {
171 	    switch (*++s) {
172 		case 'b': *bp = '\b'; break;
173 		case 'f': *bp = '\f'; break;
174 		case 'n': *bp = '\n'; break;
175 		case 't': *bp = '\t'; break;
176 
177 		case 0: s--;
178 		    /* FALLTHRU */
179 		case QUOTE:
180 		    *bp = QUOTE;
181 		    break;
182 
183 		default:
184 		    if (!isdigit ((unsigned char) *s)) {
185 			*bp++ = QUOTE;
186 			*bp = *s;
187 		    }
188 		    r = ((unsigned char) *s) != '0' ? 10 : 8;
189 		    for (i = 0; isdigit ((unsigned char) *s); s++)
190 			i *= r + ((unsigned char) *s) - '0';
191 		    s--;
192 		    *bp = toascii (i);
193 		    break;
194 	    }
195 	}
196     }
197     *bp = 0;
198 
199     return mh_xstrdup(buffer);
200 }
201 
202 /*
203  * Get the fully qualified name of the local host.
204  *
205  * If flag is 0, then use anything out of mts.conf (like localname).
206  * If flag is 1, then only use the "proper" local hostname.
207  */
208 
209 char *
LocalName(int flag)210 LocalName (int flag)
211 {
212     static char buffer0[BUFSIZ] = "";
213     static char buffer1[BUFSIZ] = "";
214     static char *buffer[] = { buffer0, buffer1 };
215     char *buf;
216     struct addrinfo hints, *res;
217 
218     if (flag < 0 || flag > 1)
219     	return NULL;
220 
221     buf = buffer[flag];
222 
223     /* check if we have cached the local name */
224     if (buf[0])
225 	return buf;
226 
227     mts_init ();
228 
229     /* check if the mts.conf file specifies a "localname" */
230     if (*localname && flag == 0) {
231 	strncpy (buf, localname, sizeof(buffer0));
232     } else {
233 	memset(buf, 0, sizeof(buffer0));
234 	/* first get our local name */
235 	gethostname (buf, sizeof(buffer0) - 1);
236 	/* now fully qualify our name */
237 
238 	memset(&hints, 0, sizeof(hints));
239 	hints.ai_flags = AI_CANONNAME;
240 	hints.ai_family = PF_UNSPEC;
241 	if (getaddrinfo(buf, NULL, &hints, &res) == 0) {
242 	    strncpy(buf, res->ai_canonname, sizeof(buffer0) - 1);
243 	    freeaddrinfo(res);
244 	}
245     }
246 
247     /*
248      * If the mts.conf file specifies a "localdomain",
249      * we append that now.  This should rarely be needed.
250      */
251     if (*localdomain) {
252 	strcat (buf, ".");
253 	strcat (buf, localdomain);
254     }
255 
256     return buf;
257 }
258 
259 
260 /*
261  * This is only for UUCP mail.  It gets the hostname
262  * as part of the UUCP "domain".
263  */
264 
265 char *
SystemName(void)266 SystemName (void)
267 {
268     static char buffer[BUFSIZ] = "";
269 
270     /* check if we have cached the system name */
271     if (buffer[0])
272 	return buffer;
273 
274     mts_init ();
275 
276     /* check if mts.conf file specifies a "systemname" */
277     if (*systemname) {
278 	strncpy (buffer, systemname, sizeof(buffer));
279 	return buffer;
280     }
281 
282     gethostname (buffer, sizeof(buffer));
283 
284     return buffer;
285 }
286 
287 
288 /*
289  * Get the username of current user
290  */
291 
292 char *
getusername(void)293 getusername (void)
294 {
295     if (username[0] == '\0')
296 	getuserinfo();
297 
298     return username;
299 }
300 
301 
302 /*
303  * Get full name of current user (typically from GECOS
304  * field of password file).
305  */
306 
307 char *
getfullname(void)308 getfullname (void)
309 {
310     if (username[0] == '\0')
311 	getuserinfo();
312 
313     return fullname;
314 }
315 
316 
317 /*
318  * Get the full local mailbox name.  This is in the form:
319  *
320  * User Name <user@name.com>
321  */
322 
323 char *
getlocalmbox(void)324 getlocalmbox (void)
325 {
326     if (username[0] == '\0')
327     	getuserinfo();
328 
329     return localmbox;
330 }
331 
332 /*
333  * Find the user's username and full name, and cache them.
334  */
335 
336 static void
getuserinfo(void)337 getuserinfo (void)
338 {
339     char *cp, *np;
340     struct passwd *pw;
341 
342     if ((pw = getpwuid (getuid ())) == NULL
343 	    || pw->pw_name == NULL
344 	    || *pw->pw_name == '\0') {
345 	strncpy (username, "unknown", sizeof(username));
346 	snprintf (fullname, sizeof(fullname), "The Unknown User-ID (%d)",
347 		(int) getuid ());
348 	return;
349     }
350 
351 
352     /* username */
353     /* If there's a Local-Mailbox profile component, try to extract
354        the username from it.  But don't try very hard, this assumes
355        the very simple User Name <user@name.com> form.
356        Note that post(8) and whom(1) use context_foil (), so they
357        won't see the profile component. */
358     if ((np = context_find("Local-Mailbox")) != NULL) {
359 	char *left_angle_bracket = strchr (np, '<');
360 	char *at_sign = strchr (np, '@');
361 	char *right_angle_bracket = strchr (np, '>');
362 
363 	strncpy(localmbox, np, sizeof(localmbox));
364 
365 	if (left_angle_bracket	&&  at_sign  &&	 right_angle_bracket) {
366 	    if (at_sign > left_angle_bracket  &&
367 		at_sign - left_angle_bracket < BUFSIZ) {
368 		strncpy(username, left_angle_bracket + 1,
369 			at_sign - left_angle_bracket - 1);
370 	    }
371 	}
372     }
373 
374     if (username[0] == '\0') {
375 	strncpy (username, pw->pw_name, sizeof(username));
376     }
377 
378     username[sizeof(username) - 1] = '\0';
379 
380     escape_local_part(username, sizeof(username));
381 
382 
383     /* fullname */
384     np = pw->pw_gecos;
385 
386     /* Get the user's real name from the GECOS field.  Stop once we hit a ',',
387        which some OSes use to separate other 'finger' information in the GECOS
388        field, like phone number. */
389     for (cp = fullname; *np != '\0' && *np != ','; *cp++ = *np++)
390 	continue;
391     *cp = '\0';
392 
393     /* The $SIGNATURE environment variable overrides the GECOS field's idea of
394        your real name. If SIGNATURE isn't set, use the Signature profile
395        setting if it exists.
396        Note that post(8) and whom(1) use context_foil (), so they
397        won't see the profile component. */
398     if ((cp = getenv ("SIGNATURE")) && *cp)
399 	strncpy (fullname, cp, sizeof(fullname));
400     else if ((cp = context_find("Signature")))
401     	strncpy (fullname, cp, sizeof(fullname));
402 
403     fullname[sizeof(fullname) - 1] = '\0';
404 
405     escape_display_name(fullname, sizeof(fullname));
406 
407 
408     /* localmbox, if not using Local-Mailbox */
409     if (localmbox[0] == '\0') {
410 	snprintf(localmbox, sizeof(localmbox), "%s <%s@%s>", fullname,
411 		 username, LocalName(0));
412     }
413 
414     localmbox[sizeof(localmbox) - 1] = '\0';
415 }
416 
417 static const char*
get_mtsconf_pathname(void)418 get_mtsconf_pathname (void)
419 {
420     const char *cp = getenv ( "MHMTSCONF" );
421     if (cp != NULL && *cp != '\0') {
422         return cp;
423     }
424     return mtsconf;
425 }
426 
427 static const char*
get_mtsuserconf_pathname(void)428 get_mtsuserconf_pathname (void)
429 {
430     const char *cp = getenv ( "MHMTSUSERCONF" );
431     if (cp != NULL && *cp != '\0') {
432         return cp;
433     }
434     return NULL;
435 }
436 
437 static void
mts_read_conf_file(FILE * fp)438 mts_read_conf_file (FILE *fp)
439 {
440     char *bp, *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 ((unsigned char) *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 }
462