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