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