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