Added -nocontentid (and -contentid, for symmetry) switch to mhbuild. This allows...
[mmh] / uip / aliasbr.c
1
2 /*
3  * aliasbr.c -- new aliasing mechanism
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <h/aliasbr.h>
14 #include <h/utils.h>
15 #include <grp.h>
16 #include <pwd.h>
17
18 static int akvis;
19 static char *akerrst;
20
21 struct aka *akahead = NULL;
22 struct aka *akatail = NULL;
23
24 struct home *homehead = NULL;
25 struct home *hometail = NULL;
26
27 /*
28  * prototypes
29  */
30 int alias (char *); 
31 int akvisible (void);
32 void init_pw (void);
33 char *akresult (struct aka *);
34 char *akvalue (char *);
35 char *akerror (int);
36
37 static  char *akval (struct aka *, char *);
38 static int aleq (char *, char *);
39 static char *scanp (char *);
40 static char *getp (char *);
41 static char *seekp (char *, char *, char **);
42 static int addfile (struct aka *, char *);
43 static int addgroup (struct aka *, char *);
44 static int addmember (struct aka *, char *);
45 static int addall (struct aka *);
46 static char *getalias (char *);
47 static void add_aka (struct aka *, char *);
48 static struct aka *akalloc (char *);
49 static struct home *hmalloc (struct passwd *);
50 struct home *seek_home (char *);
51
52
53 /* Do mh alias substitution on 's' and return the results. */
54 char *
55 akvalue (char *s)
56 {
57     register char *v;
58
59     if (akahead == NULL)
60         alias (AliasFile);
61
62     akvis = -1;
63     v = akval (akahead, s);
64     if (akvis == -1)
65         akvis = 0;
66     return v;
67 }
68
69
70 int
71 akvisible (void)
72 {
73     return akvis;
74 }
75
76
77 char *
78 akresult (struct aka *ak)
79 {
80     register char *cp = NULL, *dp, *pp;
81     register struct adr *ad;
82
83     for (ad = ak->ak_addr; ad; ad = ad->ad_next) {
84         pp = ad->ad_local ? akval (ak->ak_next, ad->ad_text)
85             : getcpy (ad->ad_text);
86
87         if (cp) {
88             dp = cp;
89             cp = concat (cp, ",", pp, NULL);
90             free (dp);
91             free (pp);
92         }
93         else
94             cp = pp;
95     }
96
97     if (akvis == -1)
98         akvis = ak->ak_visible;
99     return cp;
100 }
101
102
103 static  char *
104 akval (struct aka *ak, char *s)
105 {
106     if (!s)
107         return s;                       /* XXX */
108
109     for (; ak; ak = ak->ak_next)
110         if (aleq (s, ak->ak_name))
111             return akresult (ak);
112
113     return getcpy (s);
114 }
115
116
117 static int
118 aleq (char *string, char *aliasent)
119 {
120     register char c;
121
122     while ((c = *string++))
123         if (*aliasent == '*')
124             return 1;
125         else
126             if ((c | 040) != (*aliasent | 040))
127                 return 0;
128             else
129                 aliasent++;
130
131     return (*aliasent == 0 || *aliasent == '*');
132 }
133
134
135 int
136 alias (char *file)
137 {
138     int i;
139     register char *bp, *cp, *pp;
140     char lc, *ap;
141     register struct aka *ak = NULL;
142     register FILE *fp;
143
144     if (*file != '/'
145             && (strncmp (file, "./", 2) && strncmp (file, "../", 3)))
146         file = etcpath (file);
147     if ((fp = fopen (file, "r")) == NULL) {
148         akerrst = file;
149         return AK_NOFILE;
150     }
151
152     while (vfgets (fp, &ap) == OK) {
153         bp = ap;
154         switch (*(pp = scanp (bp))) {
155             case '<':           /* recurse a level */
156                 if (!*(cp = getp (pp + 1))) {
157                     akerrst = "'<' without alias-file";
158                     fclose (fp);
159                     return AK_ERROR;
160                 }
161                 if ((i = alias (cp)) != AK_OK) {
162                     fclose (fp);
163                     return i;
164                 }
165
166             case ':':           /* comment */
167             case ';': 
168             case '#':
169             case 0: 
170                 continue;
171         }
172
173         akerrst = bp;
174         if (!*(cp = seekp (pp, &lc, &ap))) {
175             fclose (fp);
176             return AK_ERROR;
177         }
178         if (!(ak = akalloc (cp))) {
179             fclose (fp);
180             return AK_LIMIT;
181         }
182         switch (lc) {
183             case ':': 
184                 ak->ak_visible = 0;
185                 break;
186
187             case ';': 
188                 ak->ak_visible = 1;
189                 break;
190
191             default: 
192                 fclose (fp);
193                 return AK_ERROR;
194         }
195
196         switch (*(pp = scanp (ap))) {
197             case 0:             /* EOL */
198                 fclose (fp);
199                 return AK_ERROR;
200
201             case '<':           /* read values from file */
202                 if (!*(cp = getp (pp + 1))) {
203                     fclose (fp);
204                     return AK_ERROR;
205                 }
206                 if (!addfile (ak, cp)) {
207                     fclose (fp);
208                     return AK_NOFILE;
209                 }
210                 break;
211
212             case '=':           /* UNIX group */
213                 if (!*(cp = getp (pp + 1))) {
214                     fclose (fp);
215                     return AK_ERROR;
216                 }
217                 if (!addgroup (ak, cp)) {
218                     fclose (fp);
219                     return AK_NOGROUP;
220                 }
221                 break;
222
223             case '+':           /* UNIX group members */
224                 if (!*(cp = getp (pp + 1))) {
225                     fclose (fp);
226                     return AK_ERROR;
227                 }
228                 if (!addmember (ak, cp)) {
229                     fclose (fp);
230                     return AK_NOGROUP;
231                 }
232                 break;
233
234             case '*':           /* Everyone */
235                 addall (ak);
236                 break;
237
238             default:            /* list */
239                 while ((cp = getalias (pp)))
240                     add_aka (ak, cp);
241                 break;
242         }
243     }
244
245     fclose (fp);
246     return AK_OK;
247 }
248
249
250 char *
251 akerror (int i)
252 {
253     static char buffer[BUFSIZ];
254
255     switch (i) {
256         case AK_NOFILE: 
257             snprintf (buffer, sizeof(buffer), "unable to read '%s'", akerrst);
258             break;
259
260         case AK_ERROR: 
261             snprintf (buffer, sizeof(buffer), "error in line '%s'", akerrst);
262             break;
263
264         case AK_LIMIT: 
265             snprintf (buffer, sizeof(buffer), "out of memory while on '%s'", akerrst);
266             break;
267
268         case AK_NOGROUP: 
269             snprintf (buffer, sizeof(buffer), "no such group as '%s'", akerrst);
270             break;
271
272         default: 
273             snprintf (buffer, sizeof(buffer), "unknown error (%d)", i);
274             break;
275     }
276
277     return buffer;
278 }
279
280
281 static char *
282 scanp (char *p)
283 {
284     while (isspace (*p))
285         p++;
286     return p;
287 }
288
289
290 static char *
291 getp (char *p)
292 {
293     register char  *cp = scanp (p);
294
295     p = cp;
296     while (!isspace (*cp) && *cp)
297         cp++;
298     *cp = 0;
299
300     return p;
301 }
302
303
304 static char *
305 seekp (char *p, char *c, char **a)
306 {
307     register char *cp;
308
309     p = cp = scanp (p);
310     while (!isspace (*cp) && *cp && *cp != ':' && *cp != ';')
311         cp++;
312     *c = *cp;
313     *cp++ = 0;
314     *a = cp;
315
316     return p;
317 }
318
319
320 static int
321 addfile (struct aka *ak, char *file)
322 {
323     register char *cp;
324     char buffer[BUFSIZ];
325     register FILE *fp;
326
327     if (!(fp = fopen (etcpath (file), "r"))) {
328         akerrst = file;
329         return 0;
330     }
331
332     while (fgets (buffer, sizeof buffer, fp))
333         while ((cp = getalias (buffer)))
334             add_aka (ak, cp);
335
336     fclose (fp);
337     return 1;
338 }
339
340
341 static int
342 addgroup (struct aka *ak, char *grp)
343 {
344     register char *gp;
345     register struct group *gr = getgrnam (grp);
346     register struct home *hm = NULL;
347
348     if (!gr)
349         gr = getgrgid (atoi (grp));
350     if (!gr) {
351         akerrst = grp;
352         return 0;
353     }
354
355 #ifndef DBMPWD
356     if (homehead == NULL)
357         init_pw ();
358 #endif /* DBMPWD */
359
360     while ((gp = *gr->gr_mem++))
361 #ifdef DBMPWD
362     {
363         struct passwd *pw;
364 #endif /* DBMPWD */
365         for (hm = homehead; hm; hm = hm->h_next)
366             if (!strcmp (hm->h_name, gp)) {
367                 add_aka (ak, hm->h_name);
368                 break;
369             }
370 #ifdef DBMPWD
371         if ((pw = getpwnam(gp)))
372         {
373                 hmalloc(pw);
374                 add_aka (ak, gp);
375         }
376     }
377 #endif /* DBMPWD */
378
379     return 1;
380 }
381
382
383 static int
384 addmember (struct aka *ak, char *grp)
385 {
386     gid_t gid;
387     register struct group *gr = getgrnam (grp);
388     register struct home *hm = NULL;
389
390     if (gr)
391         gid = gr->gr_gid;
392     else {
393         gid = atoi (grp);
394         gr = getgrgid (gid);
395     }
396     if (!gr) {
397         akerrst = grp;
398         return 0;
399     }
400
401 #ifndef DBMPWD
402     if (homehead == NULL)
403 #endif /* DBMPWD */
404         init_pw ();
405
406     for (hm = homehead; hm; hm = hm->h_next)
407         if (hm->h_gid == gid)
408             add_aka (ak, hm->h_name);
409
410     return 1;
411 }
412
413
414 static int
415 addall (struct aka *ak)
416 {
417     int noshell = NoShell == NULL || *NoShell == 0;
418     register struct home *hm;
419
420 #ifndef DBMPWD
421     if (homehead == NULL)
422 #endif /* DBMPWD */
423         init_pw ();
424     if (Everyone < 0)
425         Everyone = EVERYONE;
426
427     for (hm = homehead; hm; hm = hm->h_next)
428         if (hm->h_uid > Everyone
429                 && (noshell || strcmp (hm->h_shell, NoShell)))
430             add_aka (ak, hm->h_name);
431
432     return homehead != NULL;
433 }
434
435
436 static char *
437 getalias (char *addrs)
438 {
439     register char *pp, *qp;
440     static char *cp = NULL;
441
442     if (cp == NULL)
443         cp = addrs;
444     else
445         if (*cp == 0)
446             return (cp = NULL);
447
448     for (pp = cp; isspace (*pp); pp++)
449         continue;
450     if (*pp == 0)
451         return (cp = NULL);
452     for (qp = pp; *qp != 0 && *qp != ','; qp++)
453         continue;
454     if (*qp == ',')
455         *qp++ = 0;
456     for (cp = qp, qp--; qp > pp; qp--)
457         if (*qp != 0) {
458             if (isspace (*qp))
459                 *qp = 0;
460             else
461                 break;
462         }
463
464     return pp;
465 }
466
467
468 static void
469 add_aka (struct aka *ak, char *pp)
470 {
471     register struct adr *ad, *ld;
472
473     for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
474         if (!strcmp (pp, ad->ad_text))
475             return;
476
477     ad = (struct adr *) mh_xmalloc (sizeof(*ad));
478     ad->ad_text = getcpy (pp);
479     ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL;
480     ad->ad_next = NULL;
481     if (ak->ak_addr)
482         ld->ad_next = ad;
483     else
484         ak->ak_addr = ad;
485 }
486
487
488 void
489 init_pw (void)
490 {
491     register struct passwd  *pw;
492 #ifdef DBMPWD
493     static int init;
494   
495     if (!init)
496     {
497           /* if the list has yet to be initialized */
498             /* zap the list, and rebuild from scratch */
499             homehead=NULL;
500             hometail=NULL;
501             init++;
502 #endif /* DBMPWD */
503
504     setpwent ();
505
506     while ((pw = getpwent ()))
507         if (!hmalloc (pw))
508             break;
509
510     endpwent ();
511 #ifdef DBMPWD
512     }
513 #endif /* DBMPWD */
514 }
515
516
517 static struct aka *
518 akalloc (char *id)
519 {
520     register struct aka *p;
521
522     p = (struct aka *) mh_xmalloc (sizeof(*p));
523
524     p->ak_name = getcpy (id);
525     p->ak_visible = 0;
526     p->ak_addr = NULL;
527     p->ak_next = NULL;
528     if (akatail != NULL)
529         akatail->ak_next = p;
530     if (akahead == NULL)
531         akahead = p;
532     akatail = p;
533
534     return p;
535 }
536
537
538 static struct home *
539 hmalloc (struct passwd *pw)
540 {
541     register struct home *p;
542
543     p = (struct home *) mh_xmalloc (sizeof(*p));
544
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 }
560
561
562 struct home *
563 seek_home (char *name)
564 {
565     register struct home *hp;
566 #ifdef DBMPWD
567     struct passwd *pw;
568     char lname[32];
569     char *c,*c1;
570 #else  /* DBMPWD */
571
572     if (homehead == NULL)
573         init_pw ();
574 #endif /* DBMPWD */
575
576     for (hp = homehead; hp; hp = hp->h_next)
577         if (!strcasecmp (name, hp->h_name))
578             return hp;
579
580 #ifdef DBMPWD
581     /*
582      * The only place where there might be problems.
583      * This assumes that ALL usernames are kept in lowercase.
584      */
585     for (c = name, c1 = lname; *c && (c1 - lname < sizeof(lname) - 1); c++, c1++) {
586         if (isalpha(*c) && isupper(*c))
587             *c1 = tolower (*c);
588         else
589             *c1 = *c;
590     }
591     *c1 = '\0';
592     if ((pw = getpwnam(lname)))
593         return(hmalloc(pw));
594 #endif /* DBMPWD */
595         
596     return NULL;
597 }