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