Remove RCS keywords, since they no longer work after git migration.
[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 #ifndef DBMPWD
354     if (homehead == NULL)
355         init_pw ();
356 #endif /* DBMPWD */
357
358     while ((gp = *gr->gr_mem++))
359 #ifdef DBMPWD
360     {
361         struct passwd *pw;
362 #endif /* DBMPWD */
363         for (hm = homehead; hm; hm = hm->h_next)
364             if (!strcmp (hm->h_name, gp)) {
365                 add_aka (ak, hm->h_name);
366                 break;
367             }
368 #ifdef DBMPWD
369         if ((pw = getpwnam(gp)))
370         {
371                 hmalloc(pw);
372                 add_aka (ak, gp);
373         }
374     }
375 #endif /* DBMPWD */
376
377     return 1;
378 }
379
380
381 static int
382 addmember (struct aka *ak, char *grp)
383 {
384     gid_t gid;
385     register struct group *gr = getgrnam (grp);
386     register struct home *hm = NULL;
387
388     if (gr)
389         gid = gr->gr_gid;
390     else {
391         gid = atoi (grp);
392         gr = getgrgid (gid);
393     }
394     if (!gr) {
395         akerrst = grp;
396         return 0;
397     }
398
399 #ifndef DBMPWD
400     if (homehead == NULL)
401 #endif /* DBMPWD */
402         init_pw ();
403
404     for (hm = homehead; hm; hm = hm->h_next)
405         if (hm->h_gid == gid)
406             add_aka (ak, hm->h_name);
407
408     return 1;
409 }
410
411
412 static int
413 addall (struct aka *ak)
414 {
415     int noshell = NoShell == NULL || *NoShell == 0;
416     register struct home *hm;
417
418 #ifndef DBMPWD
419     if (homehead == NULL)
420 #endif /* DBMPWD */
421         init_pw ();
422     if (Everyone < 0)
423         Everyone = EVERYONE;
424
425     for (hm = homehead; hm; hm = hm->h_next)
426         if (hm->h_uid > Everyone
427                 && (noshell || strcmp (hm->h_shell, NoShell)))
428             add_aka (ak, hm->h_name);
429
430     return homehead != NULL;
431 }
432
433
434 static char *
435 getalias (char *addrs)
436 {
437     register unsigned char *pp, *qp;
438     static char *cp = NULL;
439
440     if (cp == NULL)
441         cp = addrs;
442     else
443         if (*cp == 0)
444             return (cp = NULL);
445
446     for (pp = cp; isspace (*pp); pp++)
447         continue;
448     if (*pp == 0)
449         return (cp = NULL);
450     for (qp = pp; *qp != 0 && *qp != ','; qp++)
451         continue;
452     if (*qp == ',')
453         *qp++ = 0;
454     for (cp = qp, qp--; qp > pp; qp--)
455         if (*qp != 0) {
456             if (isspace (*qp))
457                 *qp = 0;
458             else
459                 break;
460         }
461
462     return pp;
463 }
464
465
466 static void
467 add_aka (struct aka *ak, char *pp)
468 {
469     register struct adr *ad, *ld;
470
471     for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
472         if (!strcmp (pp, ad->ad_text))
473             return;
474
475     ad = (struct adr *) mh_xmalloc (sizeof(*ad));
476     ad->ad_text = getcpy (pp);
477     ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL;
478     ad->ad_next = NULL;
479     if (ak->ak_addr)
480         ld->ad_next = ad;
481     else
482         ak->ak_addr = ad;
483 }
484
485
486 void
487 init_pw (void)
488 {
489     register struct passwd  *pw;
490 #ifdef DBMPWD
491     static int init;
492   
493     if (!init)
494     {
495           /* if the list has yet to be initialized */
496             /* zap the list, and rebuild from scratch */
497             homehead=NULL;
498             hometail=NULL;
499             init++;
500 #endif /* DBMPWD */
501
502     setpwent ();
503
504     while ((pw = getpwent ()))
505         if (!hmalloc (pw))
506             break;
507
508     endpwent ();
509 #ifdef DBMPWD
510     }
511 #endif /* DBMPWD */
512 }
513
514
515 static struct aka *
516 akalloc (char *id)
517 {
518     register struct aka *p;
519
520     p = (struct aka *) mh_xmalloc (sizeof(*p));
521
522     p->ak_name = getcpy (id);
523     p->ak_visible = 0;
524     p->ak_addr = NULL;
525     p->ak_next = NULL;
526     if (akatail != NULL)
527         akatail->ak_next = p;
528     if (akahead == NULL)
529         akahead = p;
530     akatail = p;
531
532     return p;
533 }
534
535
536 static struct home *
537 hmalloc (struct passwd *pw)
538 {
539     register struct home *p;
540
541     p = (struct home *) mh_xmalloc (sizeof(*p));
542
543     p->h_name = getcpy (pw->pw_name);
544     p->h_uid = pw->pw_uid;
545     p->h_gid = pw->pw_gid;
546     p->h_home = getcpy (pw->pw_dir);
547     p->h_shell = getcpy (pw->pw_shell);
548     p->h_ngrps = 0;
549     p->h_next = NULL;
550     if (hometail != NULL)
551         hometail->h_next = p;
552     if (homehead == NULL)
553         homehead = p;
554     hometail = p;
555
556     return p;
557 }
558
559
560 struct home *
561 seek_home (char *name)
562 {
563     register struct home *hp;
564 #ifdef DBMPWD
565     struct passwd *pw;
566     char lname[32];
567     unsigned char *c;
568     char *c1;
569 #else  /* DBMPWD */
570
571     if (homehead == NULL)
572         init_pw ();
573 #endif /* DBMPWD */
574
575     for (hp = homehead; hp; hp = hp->h_next)
576         if (!mh_strcasecmp (name, hp->h_name))
577             return hp;
578
579 #ifdef DBMPWD
580     /*
581      * The only place where there might be problems.
582      * This assumes that ALL usernames are kept in lowercase.
583      */
584     for (c = name, c1 = lname; *c && (c1 - lname < sizeof(lname) - 1); c++, c1++) {
585         if (isalpha(*c) && isupper(*c))
586             *c1 = tolower (*c);
587         else
588             *c1 = *c;
589     }
590     *c1 = '\0';
591     if ((pw = getpwnam(lname)))
592         return(hmalloc(pw));
593 #endif /* DBMPWD */
594         
595     return NULL;
596 }