Removed the DBMPWD (aka. DBM) define. It was per default enabled, anyway.
[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         while ((gp = *gr->gr_mem++))
347         {
348                 struct passwd *pw;
349                 for (hm = homehead; hm; hm = hm->h_next)
350                         if (strcmp(hm->h_name, gp)==0) {
351                                 add_aka(ak, hm->h_name);
352                                 break;
353                         }
354                 if ((pw = getpwnam(gp))) {
355                         hmalloc(pw);
356                         add_aka(ak, gp);
357                 }
358         }
359
360         return 1;
361 }
362
363
364 static int
365 addmember(struct aka *ak, char *grp)
366 {
367         gid_t gid;
368         register struct group *gr = getgrnam(grp);
369         register struct home *hm = NULL;
370
371         if (gr)
372                 gid = gr->gr_gid;
373         else {
374                 gid = atoi(grp);
375                 gr = getgrgid(gid);
376         }
377         if (!gr) {
378                 akerrst = grp;
379                 return 0;
380         }
381
382         init_pw();
383
384         for (hm = homehead; hm; hm = hm->h_next)
385                 if (hm->h_gid == gid)
386                         add_aka(ak, hm->h_name);
387
388         return 1;
389 }
390
391
392 static char *
393 getalias(char *addrs)
394 {
395         register unsigned char *pp, *qp;
396         static char *cp = NULL;
397
398         if (cp == NULL)
399                 cp = addrs;
400         else
401                 if (*cp == 0)
402                         return (cp = NULL);
403
404         for (pp = cp; isspace(*pp); pp++)
405                 continue;
406         if (*pp == 0)
407                 return (cp = NULL);
408         for (qp = pp; *qp != 0 && *qp != ','; qp++)
409                 continue;
410         if (*qp == ',')
411                 *qp++ = 0;
412         for (cp = qp, qp--; qp > pp; qp--)
413                 if (*qp != 0) {
414                         if (isspace(*qp))
415                                 *qp = 0;
416                         else
417                                 break;
418                 }
419
420         return pp;
421 }
422
423
424 static void
425 add_aka(struct aka *ak, char *pp)
426 {
427         register struct adr *ad, *ld;
428
429         for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
430                 if (strcmp(pp, ad->ad_text)==0)
431                         return;
432
433         ad = (struct adr *) mh_xmalloc(sizeof(*ad));
434         ad->ad_text = getcpy(pp);
435         ad->ad_local = strchr(pp, '@') == NULL;
436         ad->ad_next = NULL;
437         if (ak->ak_addr)
438                 ld->ad_next = ad;
439         else
440                 ak->ak_addr = ad;
441 }
442
443
444 void
445 init_pw(void)
446 {
447         register struct passwd *pw;
448         static int init = 0;
449
450         if (!init) {
451                 /* read the passwd database and build a list */
452                 setpwent();
453                 while ((pw = getpwent())) {
454                         if (!hmalloc(pw)) {
455                                 break;
456                         }
457                 }
458                 endpwent();
459
460                 init++;  /* now we're initialized */
461         }
462 }
463
464
465 static struct aka *
466 akalloc(char *id)
467 {
468         register struct aka *p;
469
470         p = (struct aka *) mh_xmalloc(sizeof(*p));
471
472         p->ak_name = getcpy(id);
473         p->ak_visible = 0;
474         p->ak_addr = NULL;
475         p->ak_next = NULL;
476         if (akatail != NULL)
477                 akatail->ak_next = p;
478         if (akahead == NULL)
479                 akahead = p;
480         akatail = p;
481
482         return p;
483 }
484
485
486 static struct home *
487 hmalloc(struct passwd *pw)
488 {
489         register struct home *p;
490
491         p = (struct home *) mh_xmalloc(sizeof(*p));
492
493         p->h_name = getcpy(pw->pw_name);
494         p->h_uid = pw->pw_uid;
495         p->h_gid = pw->pw_gid;
496         p->h_home = getcpy(pw->pw_dir);
497         p->h_shell = getcpy(pw->pw_shell);
498         p->h_ngrps = 0;
499         p->h_next = NULL;
500         /* append to end */
501         if (!homehead)
502                 homehead = p;
503         if (hometail)
504                 hometail->h_next = p;
505         hometail = p;
506
507         return p;
508 }
509
510
511 struct home *
512 seek_home(char *name)
513 {
514         register struct home *hp;
515         struct passwd *pw;
516         char lname[32];
517         unsigned char *c;
518         char *c1;
519
520         for (hp = homehead; hp; hp = hp->h_next)
521                 if (!mh_strcasecmp(name, hp->h_name))
522                         return hp;
523
524         /*
525         ** The only place where there might be problems.
526         ** This assumes that ALL usernames are kept in lowercase.
527         */
528         for (c = name, c1 = lname; *c && (c1 - lname < sizeof(lname) - 1);
529                         c++, c1++) {
530                 if (isalpha(*c) && isupper(*c))
531                         *c1 = tolower(*c);
532                 else
533                         *c1 = *c;
534         }
535         *c1 = '\0';
536         if ((pw = getpwnam(lname)))
537                 return(hmalloc(pw));
538
539         return NULL;
540 }