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