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