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