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