503ec3c15f131d22f2978e53c5855894c2894e64
[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 /*
45 ** Global MailDelivery file
46 */
47 char *maildelivery = NMHETCDIR"/maildelivery";
48
49 /*
50 ** Customize the MTS settings for nmh by adjusting
51 ** the file mts.conf in the nmh etc directory.
52 */
53
54 struct bind {
55         char *keyword;
56         char **value;
57 };
58
59 static struct bind binds[] = {
60         { "maildelivery", &maildelivery },
61         { NULL, NULL }
62 };
63
64
65 /*
66 ** Read the configuration file for the nmh interface
67 ** to the mail transport system (MTS).
68 */
69
70 void
71 mts_init(char *name)
72 {
73         const char *cp;
74         FILE *fp;
75         static int inited = 0;
76
77         if (inited++ || (fp = fopen(get_mtsconf_pathname(), "r")) == NULL)
78                 return;
79         mts_read_conf_file(fp);
80         fclose(fp);
81
82         cp = get_mtsuserconf_pathname();
83         if (cp != NULL &&
84                 ((fp = fopen(get_mtsuserconf_pathname(), "r")) != NULL)) {
85                 mts_read_conf_file(fp);
86                 fclose(fp);
87         }
88 }
89
90
91 #define QUOTE  '\\'
92
93 /*
94 ** Convert escaped values, malloc some new space,
95 ** and copy string to malloc'ed memory.
96 */
97
98 static char *
99 tailor_value(unsigned char *s)
100 {
101         int i, r;
102         char *bp;
103         char buffer[BUFSIZ];
104         size_t len;
105
106         for (bp = buffer; *s; bp++, s++) {
107                 if (*s != QUOTE) {
108                         *bp = *s;
109                 } else {
110                         switch (*++s) {
111                         case 'b': *bp = '\b'; break;
112                         case 'f': *bp = '\f'; break;
113                         case 'n': *bp = '\n'; break;
114                         case 't': *bp = '\t'; break;
115
116                         case 0: s--;
117                         case QUOTE:
118                                 *bp = QUOTE;
119                                 break;
120
121                         default:
122                                 if (!isdigit(*s)) {
123                                         *bp++ = QUOTE;
124                                         *bp = *s;
125                                 }
126                                 r = *s != '0' ? 10 : 8;
127                                 for (i = 0; isdigit(*s); s++)
128                                         i = i * r + *s - '0';
129                                 s--;
130                                 *bp = toascii(i);
131                                 break;
132                         }
133                 }
134         }
135         *bp = 0;
136
137         len = strlen(buffer) + 1;
138         bp = mh_xmalloc(len);
139         memcpy(bp, buffer, len);
140
141         return bp;
142 }
143
144 /*
145 ** Get the fully qualified name of the local host.
146 */
147
148 char *
149 LocalName(void)
150 {
151         static char buffer[BUFSIZ] = "";
152         struct addrinfo hints, *res;
153 #ifdef HAVE_UNAME
154         struct utsname name;
155 #endif
156
157         /* check if we have cached the local name */
158         if (buffer[0])
159                 return buffer;
160
161         mts_init("mts");
162
163         memset(buffer, 0, sizeof(buffer));
164 #ifdef HAVE_UNAME
165         /* first get our local name */
166         uname(&name);
167         strncpy(buffer, name.nodename, sizeof(buffer) - 1);
168 #else
169         /* first get our local name */
170         gethostname(buffer, sizeof(buffer) - 1);
171 #endif
172         /* now fully qualify our name */
173
174         memset(&hints, 0, sizeof(hints));
175         hints.ai_flags = AI_CANONNAME;
176         hints.ai_family = PF_UNSPEC;
177         if (getaddrinfo(buffer, NULL, &hints, &res) == 0) {
178                 strncpy(buffer, res->ai_canonname, sizeof(buffer) - 1);
179                 freeaddrinfo(res);
180         }
181
182         return buffer;
183 }
184
185
186 /*
187 ** This is only for UUCP mail.  It gets the hostname
188 ** as part of the UUCP "domain".
189 */
190
191 char *
192 SystemName(void)
193 {
194         static char buffer[BUFSIZ] = "";
195
196 #ifdef HAVE_UNAME
197         struct utsname name;
198 #endif
199
200         /* check if we have cached the system name */
201         if (buffer[0])
202                 return buffer;
203
204         mts_init("mts");
205
206 #ifdef HAVE_UNAME
207         uname(&name);
208         strncpy(buffer, name.nodename, sizeof(buffer));
209 #else
210         gethostname(buffer, sizeof(buffer));
211 #endif
212
213         return buffer;
214 }
215
216
217 /*
218 ** Get the username of current user
219 */
220
221 char *
222 getusername(void)
223 {
224         if (username[0] == '\0')
225                 getuserinfo();
226
227         return username;
228 }
229
230
231 /*
232 ** Get full name of current user (typically from GECOS
233 ** field of password file).
234 */
235
236 char *
237 getfullname(void)
238 {
239         if (username[0] == '\0')
240                 getuserinfo();
241
242         return fullname;
243 }
244
245
246 /*
247 ** Find the user's username and full name, and cache them.
248 */
249 static void
250 getuserinfo(void)
251 {
252         register unsigned char *cp;
253         register char *np;
254         register struct passwd *pw;
255
256         if ((pw = getpwuid(getuid())) == NULL
257                 || pw->pw_name == NULL
258                 || *pw->pw_name == '\0') {
259                 strncpy(username, "unknown", sizeof(username));
260                 snprintf(fullname, sizeof(fullname), "The Unknown User-ID (%d)",
261                         (int) getuid());
262                 return;
263         }
264
265         np = pw->pw_gecos;
266
267         /*
268         ** Get the user's real name from the GECOS field.  Stop once
269         ** we hit a ',', which some OSes use to separate other 'finger'
270         ** information in the GECOS field, like phone number.
271         */
272         for (cp = fullname; *np != '\0' && *np != ',';) {
273 #ifndef BSD42
274                 *cp++ = *np++;
275 #else /* BSD42 */
276                 /*
277                 ** On BSD(-derived) systems, the system utilities that
278                 ** deal with the GECOS field (finger, mail, sendmail,
279                 ** etc.) translate any '&' character in it to the login name,
280                 ** with the first letter capitalized.  So, for instance,
281                 ** fingering a user "bob" with the GECOS field "& Jones"
282                 ** would reveal him to be "In real life: Bob Jones".
283                 ** Surprisingly, though, the OS doesn't do the translation
284                 ** for you, so we have to do it manually here.
285                 */
286                 if (*np == '&') {  /* blech! */
287                         strcpy(cp, pw->pw_name);
288                         *cp = toupper(*cp);
289                         while (*cp)
290                                 cp++;
291                         np++;
292                 } else {
293                         *cp++ = *np++;
294                 }
295 #endif /* BSD42 */
296         }
297         *cp = '\0';
298         strncpy(username, pw->pw_name, sizeof(username));
299
300         /*
301         ** The $SIGNATURE environment variable overrides the GECOS field's
302         ** idea of your real name.
303         */
304         if ((cp = getenv("SIGNATURE")) && *cp)
305                 strncpy(fullname, cp, sizeof(fullname));
306
307         if (strchr(fullname, '.')) {  /*  quote any .'s */
308                 char tmp[BUFSIZ];
309
310                 /* should quote "'s too */
311                 snprintf(tmp, sizeof(tmp), "\"%s\"", fullname);
312                 strncpy(fullname, tmp, sizeof(fullname));
313         }
314
315         return;
316 }
317
318 static const char*
319 get_mtsconf_pathname(void)
320 {
321         const char *cp = getenv( "MHMTSCONF ");
322         if (cp != NULL && *cp != '\0') {
323                 return cp;
324         }
325         return mtsconf;
326 }
327
328 static const char*
329 get_mtsuserconf_pathname(void)
330 {
331         const char *cp = getenv( "MHMTSUSERCONF" );
332         if (cp != NULL && *cp != '\0') {
333                 return cp;
334         }
335         return NULL;
336 }
337
338 static void
339 mts_read_conf_file(FILE *fp)
340 {
341         unsigned char *bp;
342         char *cp, buffer[BUFSIZ];
343         struct bind *b;
344
345         while (fgets(buffer, sizeof(buffer), fp)) {
346                 if (!(cp = strchr(buffer, '\n')))
347                         break;
348                 *cp = 0;
349                 if (*buffer == '#' || *buffer == '\0')
350                         continue;
351                 if (!(bp = strchr(buffer, ':')))
352                         break;
353                 *bp++ = 0;
354                 while (isspace(*bp))
355                         *bp++ = 0;
356
357                 for (b = binds; b->keyword; b++)
358                         if (strcmp(buffer, b->keyword)==0)
359                                 break;
360                 if (b->keyword && (cp = tailor_value(bp)))
361                         *b->value = cp;
362         }
363 }