mhl and mhbuild ignore to long lines
[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/addrsbr.h>
12 #include <h/utils.h>
13 #include <grp.h>
14 #include <pwd.h>
15 #include <ctype.h>
16
17 static int akvis;
18 static char *akerrst;
19
20 struct aka *akahead = NULL;
21 struct aka *akatail = NULL;
22
23 struct home *homehead = NULL;
24 struct home *hometail = NULL;
25
26 /*
27 ** prototypes
28 */
29 int alias(char *);
30 int akvisible(void);
31 void init_pw(void);
32 char *akresult(struct aka *);
33 char *akvalue(char *);
34 char *akerror(int);
35
36 static char *akval(struct aka *, char *);
37 static int aleq(char *, char *);
38 static char *scanp(unsigned char *);
39 static char *getp(char *);
40 static char *seekp(char *, char *, char **);
41 static int addfile(struct aka *, char *);
42 static int addgroup(struct aka *, char *);
43 static int addmember(struct aka *, char *);
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         char *v;
56
57         akvis = -1;
58         v = akval(akahead, s);
59         if (akvis == -1)
60                 akvis = 0;
61         return v;
62 }
63
64
65 int
66 akvisible(void)
67 {
68         return akvis;
69 }
70
71
72 char *
73 akresult(struct aka *ak)
74 {
75         char *cp = NULL, *dp, *pp;
76         struct adr *ad;
77
78         for (ad = ak->ak_addr; ad; ad = ad->ad_next) {
79                 pp = ad->ad_local ? akval(ak->ak_next, ad->ad_text)
80                         : mh_xstrdup(ad->ad_text);
81
82                 if (cp) {
83                         dp = cp;
84                         cp = concat(cp, ",", pp, NULL);
85                         mh_free0(&dp);
86                         mh_free0(&pp);
87                 } else
88                         cp = pp;
89         }
90
91         if (akvis == -1)
92                 akvis = ak->ak_visible;
93         return cp;
94 }
95
96
97 static char *
98 akval(struct aka *ak, char *s)
99 {
100         if (!s)
101                 return s;  /* XXX */
102
103         for (; ak; ak = ak->ak_next) {
104                 if (aleq (s, ak->ak_name)) {
105                         return akresult (ak);
106                 } else if (strchr (s, ':')) {
107                         /*
108                         ** The first address in a blind list will contain the
109                         ** alias name, so try to match, but just with just the
110                         ** address (not including the list name).  If there's a
111                         ** match, then replace the alias part with its
112                         ** expansion.
113                         */
114
115                         char *name = getname(s);
116                         char *cp = NULL;
117
118                         if (name) {
119                                 /*
120                                 ** s is of the form "Blind list: address".  If address
121                                 ** is an alias, expand it.
122                                 */
123                                 struct mailname *mp = getm(name, NULL, 0, AD_NAME, NULL);
124
125                                 if (mp  &&  mp->m_ingrp) {
126                                         char *gname = add (mp->m_gname, NULL);
127
128                                         if (gname  &&  aleq(name, ak->ak_name)) {
129                                                 /* Will leak cp. */
130                                                 cp = concat (gname, akresult (ak), NULL);
131                                                 free(gname);
132                                         }
133                                 }
134                         mnfree(mp);
135                         }
136                         /* Need to flush getname after use. */
137                         while (getname("")) continue;
138
139                         if (cp) {
140                                 return cp;
141                         }
142                 }
143         }
144
145         return mh_xstrdup(s);
146 }
147
148
149 static int
150 aleq(char *string, char *aliasent)
151 {
152         char c;
153
154         while ((c = *string++))
155                 if (*aliasent == '*')
156                         return 1;
157                 else if ((c | 040) != (*aliasent | 040))
158                         return 0;
159                 else
160                         aliasent++;
161
162         return (*aliasent == 0 || *aliasent == '*');
163 }
164
165
166 /*
167 ** file needs to be absolute or relative to cwd
168 */
169 int
170 alias(char *file)
171 {
172         int i;
173         char *bp, *cp, *pp;
174         char lc, *ap;
175         struct aka *ak = NULL;
176         FILE *fp;
177
178         if ((fp = fopen(file, "r")) == NULL) {
179                 akerrst = file;
180                 return AK_NOFILE;
181         }
182
183         while (vfgets(fp, &ap) == OK) {
184                 bp = ap;
185                 switch (*(pp = scanp(bp))) {
186                 case '<':  /* recurse a level */
187                         if (!*(cp = getp(pp + 1))) {
188                                 akerrst = "'<' without alias-file";
189                                 fclose(fp);
190                                 return AK_ERROR;
191                         }
192                         if ((i = alias(cp)) != AK_OK) {
193                                 fclose(fp);
194                                 return i;
195                         }
196
197                 case ':':  /* comment */
198                 case ';':
199                 case '#':
200                 case 0:
201                         continue;
202                 }
203
204                 akerrst = bp;
205                 if (!*(cp = seekp(pp, &lc, &ap))) {
206                         fclose(fp);
207                         return AK_ERROR;
208                 }
209                 if (!(ak = akalloc(cp))) {
210                         fclose(fp);
211                         return AK_LIMIT;
212                 }
213                 switch (lc) {
214                 case ':':
215                         ak->ak_visible = 0;
216                         break;
217
218                 case ';':
219                         ak->ak_visible = 1;
220                         break;
221
222                 default:
223                         fclose(fp);
224                         return AK_ERROR;
225                 }
226
227                 switch (*(pp = scanp(ap))) {
228                 case 0:  /* EOL */
229                         fclose(fp);
230                         return AK_ERROR;
231
232                 case '<':  /* read values from file */
233                         if (!*(cp = getp(pp + 1))) {
234                                 fclose(fp);
235                                 return AK_ERROR;
236                         }
237                         if (!addfile(ak, cp)) {
238                                 fclose(fp);
239                                 return AK_NOFILE;
240                         }
241                         break;
242
243                 case '=':  /* UNIX group */
244                         if (!*(cp = getp(pp + 1))) {
245                                 fclose(fp);
246                                 return AK_ERROR;
247                         }
248                         if (!addgroup(ak, cp)) {
249                                 fclose(fp);
250                                 return AK_NOGROUP;
251                         }
252                         break;
253
254                 case '+':  /* UNIX group members */
255                         if (!*(cp = getp(pp + 1))) {
256                                 fclose(fp);
257                                 return AK_ERROR;
258                         }
259                         if (!addmember(ak, cp)) {
260                                 fclose(fp);
261                                 return AK_NOGROUP;
262                         }
263                         break;
264
265                 default:  /* list */
266                         while ((cp = getalias(pp)))
267                                 add_aka(ak, cp);
268                         break;
269                 }
270         }
271
272         fclose(fp);
273         return AK_OK;
274 }
275
276
277 char *
278 akerror(int i)
279 {
280         static char buffer[BUFSIZ];
281
282         switch (i) {
283         case AK_NOFILE:
284                 snprintf(buffer, sizeof(buffer), "unable to read '%s'",
285                                 akerrst);
286                 break;
287
288         case AK_ERROR:
289                 snprintf(buffer, sizeof(buffer), "error in line '%s'",
290                                 akerrst);
291                 break;
292
293         case AK_LIMIT:
294                 snprintf(buffer, sizeof(buffer), "out of memory while on '%s'",
295                                 akerrst);
296                 break;
297
298         case AK_NOGROUP:
299                 snprintf(buffer, sizeof(buffer), "no such group as '%s'",
300                                 akerrst);
301                 break;
302
303         default:
304                 snprintf(buffer, sizeof(buffer), "unknown error (%d)", i);
305                 break;
306         }
307
308         return buffer;
309 }
310
311
312 static char *
313 scanp(unsigned char *p)
314 {
315         while (isspace(*p))
316                 p++;
317         return p;
318 }
319
320
321 static char *
322 getp(char *p)
323 {
324         unsigned char *cp = scanp(p);
325
326         p = cp;
327         while (!isspace(*cp) && *cp)
328                 cp++;
329         *cp = 0;
330
331         return p;
332 }
333
334
335 static char *
336 seekp(char *p, char *c, char **a)
337 {
338         unsigned char *cp;
339
340         p = cp = scanp(p);
341         while (!isspace(*cp) && *cp && *cp != ':' && *cp != ';')
342                 cp++;
343         *c = *cp;
344         *cp++ = 0;
345         *a = cp;
346
347         return p;
348 }
349
350
351 static int
352 addfile(struct aka *ak, char *file)
353 {
354         char *cp;
355         char buffer[BUFSIZ];
356         FILE *fp;
357
358         if (!(fp = fopen(etcpath(file), "r"))) {
359                 akerrst = file;
360                 return 0;
361         }
362
363         while (fgets(buffer, sizeof buffer, fp))
364                 while ((cp = getalias(buffer)))
365                         add_aka(ak, cp);
366
367         fclose(fp);
368         return 1;
369 }
370
371
372 static int
373 addgroup(struct aka *ak, char *grp)
374 {
375         char *gp;
376         struct group *gr = getgrnam(grp);
377         struct home *hm = NULL;
378
379         if (!gr)
380                 gr = getgrgid(atoi(grp));
381         if (!gr) {
382                 akerrst = grp;
383                 return 0;
384         }
385
386         while ((gp = *gr->gr_mem++))
387         {
388                 struct passwd *pw;
389                 for (hm = homehead; hm; hm = hm->h_next)
390                         if (strcmp(hm->h_name, gp)==0) {
391                                 add_aka(ak, hm->h_name);
392                                 break;
393                         }
394                 if ((pw = getpwnam(gp))) {
395                         hmalloc(pw);
396                         add_aka(ak, gp);
397                 }
398         }
399
400         return 1;
401 }
402
403
404 static int
405 addmember(struct aka *ak, char *grp)
406 {
407         gid_t gid;
408         struct group *gr = getgrnam(grp);
409         struct home *hm = NULL;
410
411         if (gr)
412                 gid = gr->gr_gid;
413         else {
414                 gid = atoi(grp);
415                 gr = getgrgid(gid);
416         }
417         if (!gr) {
418                 akerrst = grp;
419                 return 0;
420         }
421
422         init_pw();
423
424         for (hm = homehead; hm; hm = hm->h_next)
425                 if (hm->h_gid == gid)
426                         add_aka(ak, hm->h_name);
427
428         return 1;
429 }
430
431
432 static char *
433 getalias(char *addrs)
434 {
435         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         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 = mh_xcalloc(1, sizeof(*ad));
474         ad->ad_text = mh_xstrdup(pp);
475         ad->ad_local = 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         struct passwd *pw;
488         static int init = 0;
489
490         if (!init) {
491                 /* read the passwd database and build a list */
492                 setpwent();
493                 while ((pw = getpwent())) {
494                         if (!hmalloc(pw)) {
495                                 break;
496                         }
497                 }
498                 endpwent();
499
500                 init++;  /* now we're initialized */
501         }
502 }
503
504
505 static struct aka *
506 akalloc(char *id)
507 {
508         struct aka *p;
509
510         p = mh_xcalloc(1, sizeof(*p));
511
512         p->ak_name = mh_xstrdup(id);
513         p->ak_visible = 0;
514         p->ak_addr = NULL;
515         p->ak_next = NULL;
516         if (akatail != NULL)
517                 akatail->ak_next = p;
518         if (akahead == NULL)
519                 akahead = p;
520         akatail = p;
521
522         return p;
523 }
524
525
526 static struct home *
527 hmalloc(struct passwd *pw)
528 {
529         struct home *p;
530
531         p = mh_xcalloc(1, sizeof(*p));
532
533         p->h_name = mh_xstrdup(pw->pw_name);
534         p->h_uid = pw->pw_uid;
535         p->h_gid = pw->pw_gid;
536         p->h_home = mh_xstrdup(pw->pw_dir);
537         p->h_shell = mh_xstrdup(pw->pw_shell);
538         p->h_ngrps = 0;
539         p->h_next = NULL;
540         /* append to end */
541         if (!homehead)
542                 homehead = p;
543         if (hometail)
544                 hometail->h_next = p;
545         hometail = p;
546
547         return p;
548 }
549
550
551 struct home *
552 seek_home(char *name)
553 {
554         struct home *hp;
555         struct passwd *pw;
556         char lname[32];
557         unsigned char *c;
558         char *c1;
559
560         for (hp = homehead; hp; hp = hp->h_next)
561                 if (!mh_strcasecmp(name, hp->h_name))
562                         return hp;
563
564         /*
565         ** The only place where there might be problems.
566         ** This assumes that ALL usernames are kept in lowercase.
567         */
568         for (c = name, c1 = lname; *c && (c1 - lname < (int)sizeof(lname) - 1);
569                         c++, c1++) {
570                 if (isalpha(*c) && isupper(*c))
571                         *c1 = tolower(*c);
572                 else
573                         *c1 = *c;
574         }
575         *c1 = '\0';
576         if ((pw = getpwnam(lname)))
577                 return(hmalloc(pw));
578
579         return NULL;
580 }