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