]> diplodocus.org Git - nmh/blob - uip/aliasbr.c
man/*.man: Use italic for emphasis, not bold or SHOUTING.
[nmh] / uip / aliasbr.c
1
2 /*
3 * aliasbr.c -- new aliasing mechanism
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>
11 #include <h/aliasbr.h>
12 #include <h/addrsbr.h>
13 #include <h/utils.h>
14 #include <grp.h>
15 #include <pwd.h>
16
17 static int akvis;
18 static char *akerrst;
19
20 struct aka *akahead = NULL;
21 struct aka *akatail = NULL;
22
23 struct home *homehead = NULL;
24 struct home *hometail = NULL;
25
26 /*
27 * prototypes
28 */
29 int alias (char *);
30 int akvisible (void);
31 void init_pw (void);
32 char *akresult (struct aka *);
33 char *akvalue (char *);
34 char *akerror (int);
35
36 static char *akval (struct aka *, char *);
37 static int aleq (char *, char *);
38 static char *scanp (char *);
39 static char *getp (char *);
40 static char *seekp (char *, char *, char **);
41 static int addfile (struct aka *, char *);
42 static int addgroup (struct aka *, char *);
43 static int addmember (struct aka *, char *);
44 static char *getalias (char *);
45 static void add_aka (struct aka *, char *);
46 static struct aka *akalloc (char *);
47 static struct home *hmalloc (struct passwd *);
48
49
50 /* Do mh alias substitution on 's' and return the results. */
51 char *
52 akvalue (char *s)
53 {
54 char *v;
55
56 if (akahead == NULL)
57 alias (AliasFile);
58
59 akvis = -1;
60 v = akval (akahead, s);
61 if (akvis == -1)
62 akvis = 0;
63 return v;
64 }
65
66
67 int
68 akvisible (void)
69 {
70 return akvis;
71 }
72
73
74 char *
75 akresult (struct aka *ak)
76 {
77 char *cp = NULL, *dp, *pp;
78 struct adr *ad;
79
80 for (ad = ak->ak_addr; ad; ad = ad->ad_next) {
81 pp = ad->ad_local ? akval (ak->ak_next, ad->ad_text)
82 : getcpy (ad->ad_text);
83
84 if (cp) {
85 dp = cp;
86 cp = concat (cp, ",", pp, NULL);
87 free (dp);
88 free (pp);
89 }
90 else
91 cp = pp;
92 }
93
94 if (akvis == -1)
95 akvis = ak->ak_visible;
96 return cp;
97 }
98
99
100 static char *
101 akval (struct aka *ak, char *s)
102 {
103 if (!s)
104 return s; /* XXX */
105
106 /* It'd be tempting to check for a trailing semicolon and remove
107 it. But that would break the EXMH alias parser on what would
108 then be valid expressions:
109 http://lists.gnu.org/archive/html/nmh-workers/2012-10/msg00039.html
110 */
111
112 for (; ak; ak = ak->ak_next) {
113 if (aleq (s, ak->ak_name)) {
114 return akresult (ak);
115 }
116
117 if (strchr (s, ':')) {
118 /* The first address in a blind list will contain the
119 alias name, so try to match, but just with just the
120 address (not including the list name). If there's a
121 match, then replace the alias part with its
122 expansion. */
123
124 char *name = getname (s);
125 char *cp = NULL;
126
127 if (name) {
128 /* s is of the form "Blind list: address". If address
129 is an alias, expand it. */
130 struct mailname *mp = getm (name, NULL, 0, NULL, 0);
131
132 if (mp && mp->m_ingrp) {
133 char *gname = add (mp->m_gname, NULL);
134
135 /* FIXME: gname must be true; add() never returns NULL.
136 * Is some other test required? */
137 if (gname && aleq (name, ak->ak_name)) {
138 /* Will leak cp. */
139 cp = concat (gname, akresult (ak), NULL);
140 free (gname);
141 }
142 }
143
144 mnfree (mp);
145 }
146
147 /* Need to flush getname after use. */
148 while (getname ("")) continue;
149
150 if (cp) {
151 return cp;
152 }
153 }
154 }
155
156 return mh_xstrdup(s);
157 }
158
159
160 static int
161 aleq (char *string, char *aliasent)
162 {
163 char c;
164
165 while ((c = *string++)) {
166 if (*aliasent == '*')
167 return 1;
168 if (tolower((unsigned char)c) != tolower((unsigned char)*aliasent))
169 return 0;
170 aliasent++;
171 }
172
173 return (*aliasent == 0 || *aliasent == '*');
174 }
175
176
177 int
178 alias (char *file)
179 {
180 int i;
181 char *bp, *cp, *pp;
182 char lc, *ap;
183 struct aka *ak = NULL;
184 FILE *fp;
185
186 if (*file != '/'
187 && !has_prefix(file, "./") && !has_prefix(file, "../"))
188 file = etcpath (file);
189 if ((fp = fopen (file, "r")) == NULL) {
190 akerrst = file;
191 return AK_NOFILE;
192 }
193
194 while (vfgets (fp, &ap) == OK) {
195 bp = ap;
196 switch (*(pp = scanp (bp))) {
197 case '<': /* recurse a level */
198 if (!*(cp = getp (pp + 1))) {
199 akerrst = "'<' without alias-file";
200 fclose (fp);
201 return AK_ERROR;
202 }
203 if ((i = alias (cp)) != AK_OK) {
204 fclose (fp);
205 return i;
206 }
207 /* FALLTHRU */
208 case ':': /* comment */
209 case ';':
210 case '#':
211 case 0:
212 continue;
213 }
214
215 akerrst = bp;
216 if (!*(cp = seekp (pp, &lc, &ap))) {
217 fclose (fp);
218 return AK_ERROR;
219 }
220 if (!(ak = akalloc (cp))) {
221 fclose (fp);
222 return AK_LIMIT;
223 }
224 switch (lc) {
225 case ':':
226 ak->ak_visible = 0;
227 break;
228
229 case ';':
230 ak->ak_visible = 1;
231 break;
232
233 default:
234 fclose (fp);
235 return AK_ERROR;
236 }
237
238 switch (*(pp = scanp (ap))) {
239 case 0: /* EOL */
240 fclose (fp);
241 return AK_ERROR;
242
243 case '<': /* read values from file */
244 if (!*(cp = getp (pp + 1))) {
245 fclose (fp);
246 return AK_ERROR;
247 }
248 if (!addfile (ak, cp)) {
249 fclose (fp);
250 return AK_NOFILE;
251 }
252 break;
253
254 case '=': /* UNIX group */
255 if (!*(cp = getp (pp + 1))) {
256 fclose (fp);
257 return AK_ERROR;
258 }
259 if (!addgroup (ak, cp)) {
260 fclose (fp);
261 return AK_NOGROUP;
262 }
263 break;
264
265 case '+': /* UNIX group members */
266 if (!*(cp = getp (pp + 1))) {
267 fclose (fp);
268 return AK_ERROR;
269 }
270 if (!addmember (ak, cp)) {
271 fclose (fp);
272 return AK_NOGROUP;
273 }
274 break;
275
276 default: /* list */
277 while ((cp = getalias (pp)))
278 add_aka (ak, cp);
279 break;
280 }
281 }
282
283 fclose (fp);
284 return AK_OK;
285 }
286
287
288 char *
289 akerror (int i)
290 {
291 static char buffer[BUFSIZ];
292
293 switch (i) {
294 case AK_NOFILE:
295 snprintf (buffer, sizeof(buffer), "unable to read '%s'", akerrst);
296 break;
297
298 case AK_ERROR:
299 snprintf (buffer, sizeof(buffer), "error in line '%s'", akerrst);
300 break;
301
302 case AK_LIMIT:
303 snprintf (buffer, sizeof(buffer), "out of memory while on '%s'", akerrst);
304 break;
305
306 case AK_NOGROUP:
307 snprintf (buffer, sizeof(buffer), "no such group as '%s'", akerrst);
308 break;
309
310 default:
311 snprintf (buffer, sizeof(buffer), "unknown error (%d)", i);
312 break;
313 }
314
315 return buffer;
316 }
317
318
319 static char *
320 scanp (char *p)
321 {
322 while (isspace ((unsigned char) *p))
323 p++;
324 return p;
325 }
326
327
328 static char *
329 getp (char *p)
330 {
331 char *cp = scanp (p);
332
333 p = cp;
334 while (!isspace ((unsigned char) *cp) && *cp)
335 cp++;
336 *cp = 0;
337
338 return p;
339 }
340
341
342 static char *
343 seekp (char *p, char *c, char **a)
344 {
345 char *cp;
346
347 p = cp = scanp (p);
348 while (!isspace ((unsigned char) *cp) && *cp && *cp != ':' && *cp != ';')
349 cp++;
350 *c = *cp;
351 *cp++ = 0;
352 *a = cp;
353
354 return p;
355 }
356
357
358 static int
359 addfile (struct aka *ak, char *file)
360 {
361 char *cp;
362 char buffer[BUFSIZ];
363 FILE *fp;
364
365 if (!(fp = fopen (etcpath (file), "r"))) {
366 akerrst = file;
367 return 0;
368 }
369
370 while (fgets (buffer, sizeof buffer, fp))
371 while ((cp = getalias (buffer)))
372 add_aka (ak, cp);
373
374 fclose (fp);
375 return 1;
376 }
377
378
379 static int
380 addgroup (struct aka *ak, char *grp)
381 {
382 char *gp;
383 struct group *gr = getgrnam (grp);
384 struct home *hm = NULL;
385
386 if (!gr)
387 gr = getgrgid (atoi (grp));
388 if (!gr) {
389 akerrst = grp;
390 return 0;
391 }
392
393 while ((gp = *gr->gr_mem++))
394 {
395 struct passwd *pw;
396 for (hm = homehead; hm; hm = hm->h_next)
397 if (!strcmp (hm->h_name, gp)) {
398 add_aka (ak, hm->h_name);
399 break;
400 }
401 if ((pw = getpwnam(gp)))
402 {
403 hmalloc(pw);
404 add_aka (ak, gp);
405 }
406 }
407
408 return 1;
409 }
410
411
412 static int
413 addmember (struct aka *ak, char *grp)
414 {
415 gid_t gid;
416 struct group *gr = getgrnam (grp);
417 struct home *hm = NULL;
418
419 if (gr)
420 gid = gr->gr_gid;
421 else {
422 gid = atoi (grp);
423 gr = getgrgid (gid);
424 }
425 if (!gr) {
426 akerrst = grp;
427 return 0;
428 }
429
430 init_pw ();
431
432 for (hm = homehead; hm; hm = hm->h_next)
433 if (hm->h_gid == gid)
434 add_aka (ak, hm->h_name);
435
436 return 1;
437 }
438
439
440 static char *
441 getalias (char *addrs)
442 {
443 char *pp, *qp;
444 static char *cp = NULL;
445
446 if (cp == NULL)
447 cp = addrs;
448 else
449 if (*cp == 0)
450 return (cp = NULL);
451
452 /* Remove leading any space from the address. */
453 for (pp = cp; isspace ((unsigned char) *pp); pp++)
454 continue;
455 if (*pp == 0)
456 return (cp = NULL);
457 /* Find the end of the address. */
458 for (qp = pp; *qp != 0 && *qp != ','; qp++)
459 continue;
460 /* Set cp to point to the remainder of the addresses. */
461 if (*qp == ',')
462 *qp++ = 0;
463 for (cp = qp, qp--; qp > pp; qp--)
464 if (*qp != 0) {
465 if (isspace ((unsigned char) *qp))
466 *qp = 0;
467 else
468 break;
469 }
470
471 return pp;
472 }
473
474
475 static void
476 add_aka (struct aka *ak, char *pp)
477 {
478 struct adr *ad, *ld;
479
480 for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
481 if (!strcmp (pp, ad->ad_text))
482 return;
483
484 NEW(ad);
485 ad->ad_text = mh_xstrdup(pp);
486 ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL;
487 ad->ad_next = NULL;
488 if (ak->ak_addr)
489 ld->ad_next = ad;
490 else
491 ak->ak_addr = ad;
492 }
493
494
495 void
496 init_pw (void)
497 {
498 struct passwd *pw;
499 static int init;
500
501 if (!init)
502 {
503 /* if the list has yet to be initialized */
504 /* zap the list, and rebuild from scratch */
505 homehead=NULL;
506 hometail=NULL;
507 init++;
508
509 setpwent ();
510
511 while ((pw = getpwent ()))
512 if (!hmalloc (pw))
513 break;
514
515 endpwent ();
516 }
517 }
518
519
520 static struct aka *
521 akalloc (char *id)
522 {
523 struct aka *p;
524
525 NEW(p);
526 p->ak_name = getcpy (id);
527 p->ak_visible = 0;
528 p->ak_addr = NULL;
529 p->ak_next = NULL;
530 if (akatail != NULL)
531 akatail->ak_next = p;
532 if (akahead == NULL)
533 akahead = p;
534 akatail = p;
535
536 return p;
537 }
538
539
540 static struct home *
541 hmalloc (struct passwd *pw)
542 {
543 struct home *p;
544
545 NEW(p);
546 p->h_name = getcpy (pw->pw_name);
547 p->h_uid = pw->pw_uid;
548 p->h_gid = pw->pw_gid;
549 p->h_home = getcpy (pw->pw_dir);
550 p->h_shell = getcpy (pw->pw_shell);
551 p->h_ngrps = 0;
552 p->h_next = NULL;
553 if (hometail != NULL)
554 hometail->h_next = p;
555 if (homehead == NULL)
556 homehead = p;
557 hometail = p;
558
559 return p;
560 }