Add %(unmailto) format function for List-Post headers
[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 #include <ctype.h>
15
16 static int akvis;
17 static char *akerrst;
18
19 struct aka *akahead = NULL;
20 struct aka *akatail = NULL;
21
22 struct home *homehead = NULL;
23 struct home *hometail = NULL;
24
25 /*
26 ** prototypes
27 */
28 int alias(char *);
29 int akvisible(void);
30 void init_pw(void);
31 char *akresult(struct aka *);
32 char *akvalue(char *);
33 char *akerror(int);
34
35 static char *akval(struct aka *, char *);
36 static int aleq(char *, char *);
37 static char *scanp(unsigned char *);
38 static char *getp(char *);
39 static char *seekp(char *, char *, char **);
40 static int addfile(struct aka *, char *);
41 static int addgroup(struct aka *, char *);
42 static int addmember(struct aka *, char *);
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         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         char *cp = NULL, *dp, *pp;
75         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                         : mh_xstrdup(ad->ad_text);
80
81                 if (cp) {
82                         dp = cp;
83                         cp = concat(cp, ",", pp, NULL);
84                         mh_free0(&dp);
85                         mh_free0(&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 mh_xstrdup(s);
107 }
108
109
110 static int
111 aleq(char *string, char *aliasent)
112 {
113         char c;
114
115         while ((c = *string++))
116                 if (*aliasent == '*')
117                         return 1;
118                 else if ((c | 040) != (*aliasent | 040))
119                         return 0;
120                 else
121                         aliasent++;
122
123         return (*aliasent == 0 || *aliasent == '*');
124 }
125
126
127 /*
128 ** file needs to be absolute or relative to cwd
129 */
130 int
131 alias(char *file)
132 {
133         int i;
134         char *bp, *cp, *pp;
135         char lc, *ap;
136         struct aka *ak = NULL;
137         FILE *fp;
138
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                 default:  /* list */
227                         while ((cp = getalias(pp)))
228                                 add_aka(ak, cp);
229                         break;
230                 }
231         }
232
233         fclose(fp);
234         return AK_OK;
235 }
236
237
238 char *
239 akerror(int i)
240 {
241         static char buffer[BUFSIZ];
242
243         switch (i) {
244         case AK_NOFILE:
245                 snprintf(buffer, sizeof(buffer), "unable to read '%s'",
246                                 akerrst);
247                 break;
248
249         case AK_ERROR:
250                 snprintf(buffer, sizeof(buffer), "error in line '%s'",
251                                 akerrst);
252                 break;
253
254         case AK_LIMIT:
255                 snprintf(buffer, sizeof(buffer), "out of memory while on '%s'",
256                                 akerrst);
257                 break;
258
259         case AK_NOGROUP:
260                 snprintf(buffer, sizeof(buffer), "no such group as '%s'",
261                                 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         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         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         char *cp;
316         char buffer[BUFSIZ];
317         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         char *gp;
337         struct group *gr = getgrnam(grp);
338         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         while ((gp = *gr->gr_mem++))
348         {
349                 struct passwd *pw;
350                 for (hm = homehead; hm; hm = hm->h_next)
351                         if (strcmp(hm->h_name, gp)==0) {
352                                 add_aka(ak, hm->h_name);
353                                 break;
354                         }
355                 if ((pw = getpwnam(gp))) {
356                         hmalloc(pw);
357                         add_aka(ak, gp);
358                 }
359         }
360
361         return 1;
362 }
363
364
365 static int
366 addmember(struct aka *ak, char *grp)
367 {
368         gid_t gid;
369         struct group *gr = getgrnam(grp);
370         struct home *hm = NULL;
371
372         if (gr)
373                 gid = gr->gr_gid;
374         else {
375                 gid = atoi(grp);
376                 gr = getgrgid(gid);
377         }
378         if (!gr) {
379                 akerrst = grp;
380                 return 0;
381         }
382
383         init_pw();
384
385         for (hm = homehead; hm; hm = hm->h_next)
386                 if (hm->h_gid == gid)
387                         add_aka(ak, hm->h_name);
388
389         return 1;
390 }
391
392
393 static char *
394 getalias(char *addrs)
395 {
396         unsigned char *pp, *qp;
397         static char *cp = NULL;
398
399         if (cp == NULL)
400                 cp = addrs;
401         else
402                 if (*cp == 0)
403                         return (cp = NULL);
404
405         for (pp = cp; isspace(*pp); pp++)
406                 continue;
407         if (*pp == 0)
408                 return (cp = NULL);
409         for (qp = pp; *qp != 0 && *qp != ','; qp++)
410                 continue;
411         if (*qp == ',')
412                 *qp++ = 0;
413         for (cp = qp, qp--; qp > pp; qp--)
414                 if (*qp != 0) {
415                         if (isspace(*qp))
416                                 *qp = 0;
417                         else
418                                 break;
419                 }
420
421         return pp;
422 }
423
424
425 static void
426 add_aka(struct aka *ak, char *pp)
427 {
428         struct adr *ad, *ld;
429
430         for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
431                 if (strcmp(pp, ad->ad_text)==0)
432                         return;
433
434         ad = mh_xcalloc(1, sizeof(*ad));
435         ad->ad_text = mh_xstrdup(pp);
436         ad->ad_local = strchr(pp, '@') == NULL;
437         ad->ad_next = NULL;
438         if (ak->ak_addr)
439                 ld->ad_next = ad;
440         else
441                 ak->ak_addr = ad;
442 }
443
444
445 void
446 init_pw(void)
447 {
448         struct passwd *pw;
449         static int init = 0;
450
451         if (!init) {
452                 /* read the passwd database and build a list */
453                 setpwent();
454                 while ((pw = getpwent())) {
455                         if (!hmalloc(pw)) {
456                                 break;
457                         }
458                 }
459                 endpwent();
460
461                 init++;  /* now we're initialized */
462         }
463 }
464
465
466 static struct aka *
467 akalloc(char *id)
468 {
469         struct aka *p;
470
471         p = mh_xcalloc(1, sizeof(*p));
472
473         p->ak_name = mh_xstrdup(id);
474         p->ak_visible = 0;
475         p->ak_addr = NULL;
476         p->ak_next = NULL;
477         if (akatail != NULL)
478                 akatail->ak_next = p;
479         if (akahead == NULL)
480                 akahead = p;
481         akatail = p;
482
483         return p;
484 }
485
486
487 static struct home *
488 hmalloc(struct passwd *pw)
489 {
490         struct home *p;
491
492         p = mh_xcalloc(1, sizeof(*p));
493
494         p->h_name = mh_xstrdup(pw->pw_name);
495         p->h_uid = pw->pw_uid;
496         p->h_gid = pw->pw_gid;
497         p->h_home = mh_xstrdup(pw->pw_dir);
498         p->h_shell = mh_xstrdup(pw->pw_shell);
499         p->h_ngrps = 0;
500         p->h_next = NULL;
501         /* append to end */
502         if (!homehead)
503                 homehead = p;
504         if (hometail)
505                 hometail->h_next = p;
506         hometail = p;
507
508         return p;
509 }
510
511
512 struct home *
513 seek_home(char *name)
514 {
515         struct home *hp;
516         struct passwd *pw;
517         char lname[32];
518         unsigned char *c;
519         char *c1;
520
521         for (hp = homehead; hp; hp = hp->h_next)
522                 if (!mh_strcasecmp(name, hp->h_name))
523                         return hp;
524
525         /*
526         ** The only place where there might be problems.
527         ** This assumes that ALL usernames are kept in lowercase.
528         */
529         for (c = name, c1 = lname; *c && (c1 - lname < (int)sizeof(lname) - 1);
530                         c++, c1++) {
531                 if (isalpha(*c) && isupper(*c))
532                         *c1 = tolower(*c);
533                 else
534                         *c1 = *c;
535         }
536         *c1 = '\0';
537         if ((pw = getpwnam(lname)))
538                 return(hmalloc(pw));
539
540         return NULL;
541 }