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