]> diplodocus.org Git - nmh/blob - uip/aliasbr.c
vector.c: Move interface to own file.
[nmh] / uip / aliasbr.c
1 /* aliasbr.c -- new aliasing mechanism
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "h/aliasbr.h"
10 #include "h/addrsbr.h"
11 #include "h/utils.h"
12 #include <pwd.h>
13
14 static int akvis;
15 static char *akerrst;
16
17 struct aka *akahead = NULL;
18 struct aka *akatail = NULL;
19
20 /*
21 * prototypes
22 */
23 static char *akval (struct aka *, char *);
24 static bool aleq (char *, char *) PURE;
25 static char *scanp (char *) PURE;
26 static char *getp (char *);
27 static char *seekp (char *, char *, char **);
28 static int addfile (struct aka *, char *);
29 static char *getalias (char *);
30 static void add_aka (struct aka *, char *);
31 static struct aka *akalloc (char *);
32
33
34 /* Do mh alias substitution on 's' and return the results. */
35 char *
36 akvalue (char *s)
37 {
38 char *v;
39
40 if (akahead == NULL)
41 alias (AliasFile);
42
43 akvis = -1;
44 v = akval (akahead, s);
45 if (akvis == -1)
46 akvis = 0;
47 return v;
48 }
49
50
51 int
52 akvisible (void)
53 {
54 return akvis;
55 }
56
57
58 char *
59 akresult (struct aka *ak)
60 {
61 char *cp = NULL, *dp, *pp;
62 struct adr *ad;
63
64 for (ad = ak->ak_addr; ad; ad = ad->ad_next) {
65 pp = ad->ad_local ? akval (ak->ak_next, ad->ad_text)
66 : getcpy (ad->ad_text);
67
68 if (cp) {
69 dp = cp;
70 cp = concat (cp, ",", pp, NULL);
71 free (dp);
72 free (pp);
73 }
74 else
75 cp = pp;
76 }
77
78 if (akvis == -1)
79 akvis = ak->ak_visible;
80 return cp;
81 }
82
83
84 static char *
85 akval (struct aka *ak, char *s)
86 {
87 if (!s)
88 return s; /* XXX */
89
90 /* It'd be tempting to check for a trailing semicolon and remove
91 it. But that would break the EXMH alias parser on what would
92 then be valid expressions:
93 http://lists.gnu.org/archive/html/nmh-workers/2012-10/msg00039.html
94 */
95
96 for (; ak; ak = ak->ak_next) {
97 if (aleq (s, ak->ak_name)) {
98 return akresult (ak);
99 }
100
101 if (strchr (s, ':')) {
102 /* The first address in a blind list will contain the
103 alias name, so try to match, but just with just the
104 address (not including the list name). If there's a
105 match, then replace the alias part with its
106 expansion. */
107
108 char *name = getname (s);
109 char *cp = NULL;
110
111 if (name) {
112 /* s is of the form "Blind list: address". If address
113 is an alias, expand it. */
114 struct mailname *mp = getm (name, NULL, 0, NULL, 0);
115
116 if (mp && mp->m_ingrp) {
117 char *gname = mh_xstrdup(FENDNULL(mp->m_gname));
118
119 /* FIXME: gname must be true; add() never returns NULL.
120 * Is some other test required? */
121 if (gname && aleq (name, ak->ak_name)) {
122 /* Will leak cp. */
123 cp = concat (gname, akresult (ak), NULL);
124 free (gname);
125 }
126 }
127
128 mnfree (mp);
129 }
130
131 /* Need to flush getname after use. */
132 while (getname ("")) continue;
133
134 if (cp) {
135 return cp;
136 }
137 }
138 }
139
140 return mh_xstrdup(s);
141 }
142
143
144 static bool
145 aleq (char *string, char *aliasent)
146 {
147 char c;
148
149 while ((c = *string++)) {
150 if (*aliasent == '*')
151 return true;
152 if (tolower((unsigned char)c) != tolower((unsigned char)*aliasent))
153 return false;
154 aliasent++;
155 }
156
157 return *aliasent == 0 || *aliasent == '*';
158 }
159
160
161 int
162 alias (char *file)
163 {
164 int i;
165 char *bp, *cp, *pp;
166 char lc, *ap;
167 struct aka *ak = NULL;
168 FILE *fp;
169
170 if (*file != '/'
171 && !has_prefix(file, "./") && !has_prefix(file, "../"))
172 file = etcpath (file);
173 if ((fp = fopen (file, "r")) == NULL) {
174 akerrst = file;
175 return AK_NOFILE;
176 }
177
178 while (vfgets (fp, &ap) == OK) {
179 bp = ap;
180 switch (*(pp = scanp (bp))) {
181 case '<': /* recurse a level */
182 if (!*(cp = getp (pp + 1))) {
183 akerrst = "'<' without alias-file";
184 fclose (fp);
185 return AK_ERROR;
186 }
187 if ((i = alias (cp)) != AK_OK) {
188 fclose (fp);
189 return i;
190 }
191 continue;
192 case ':': /* comment */
193 case ';':
194 case '#':
195 case 0:
196 continue;
197 }
198
199 akerrst = bp;
200 if (!*(cp = seekp (pp, &lc, &ap))) {
201 fclose (fp);
202 return AK_ERROR;
203 }
204 if (!(ak = akalloc (cp))) {
205 fclose (fp);
206 return AK_LIMIT;
207 }
208 switch (lc) {
209 case ':':
210 ak->ak_visible = false;
211 break;
212
213 case ';':
214 ak->ak_visible = true;
215 break;
216
217 default:
218 fclose (fp);
219 return AK_ERROR;
220 }
221
222 switch (*(pp = scanp (ap))) {
223 case 0: /* EOL */
224 fclose (fp);
225 return AK_ERROR;
226
227 case '<': /* read values from file */
228 if (!*(cp = getp (pp + 1))) {
229 fclose (fp);
230 return AK_ERROR;
231 }
232 if (!addfile (ak, cp)) {
233 fclose (fp);
234 return AK_NOFILE;
235 }
236 break;
237
238 default: /* list */
239 while ((cp = getalias (pp)))
240 add_aka (ak, cp);
241 break;
242 }
243 }
244
245 fclose (fp);
246 return AK_OK;
247 }
248
249
250 char *
251 akerror (int i)
252 {
253 static char buffer[BUFSIZ];
254
255 switch (i) {
256 case AK_NOFILE:
257 snprintf (buffer, sizeof(buffer), "unable to read '%s'", akerrst);
258 break;
259
260 case AK_ERROR:
261 snprintf (buffer, sizeof(buffer), "error in line '%s'", akerrst);
262 break;
263
264 case AK_LIMIT:
265 snprintf (buffer, sizeof(buffer), "out of memory while on '%s'", akerrst);
266 break;
267
268 default:
269 snprintf (buffer, sizeof(buffer), "unknown error (%d)", i);
270 break;
271 }
272
273 return buffer;
274 }
275
276
277 static char *
278 scanp (char *p)
279 {
280 while (isspace ((unsigned char) *p))
281 p++;
282 return p;
283 }
284
285
286 static char *
287 getp (char *p)
288 {
289 char *cp = scanp (p);
290
291 p = cp;
292 while (!isspace ((unsigned char) *cp) && *cp)
293 cp++;
294 *cp = 0;
295
296 return p;
297 }
298
299
300 static char *
301 seekp (char *p, char *c, char **a)
302 {
303 char *cp;
304
305 p = cp = scanp (p);
306 while (!isspace ((unsigned char) *cp) && *cp && *cp != ':' && *cp != ';')
307 cp++;
308 *c = *cp;
309 *cp++ = 0;
310 *a = cp;
311
312 return p;
313 }
314
315
316 static int
317 addfile (struct aka *ak, char *file)
318 {
319 char *cp;
320 char buffer[BUFSIZ];
321 FILE *fp;
322
323 if (!(fp = fopen (etcpath (file), "r"))) {
324 akerrst = file;
325 return 0;
326 }
327
328 while (fgets (buffer, sizeof buffer, fp))
329 while ((cp = getalias (buffer)))
330 add_aka (ak, cp);
331
332 fclose (fp);
333 return 1;
334 }
335
336
337 static char *
338 getalias (char *addrs)
339 {
340 char *pp, *qp;
341 static char *cp = NULL;
342
343 if (cp == NULL)
344 cp = addrs;
345 else if (*cp == 0)
346 return cp = NULL;
347
348 /* Remove leading any space from the address. */
349 for (pp = cp; isspace ((unsigned char) *pp); pp++)
350 continue;
351 if (*pp == 0)
352 return cp = NULL;
353 /* Find the end of the address. */
354 for (qp = pp; *qp != 0 && *qp != ','; qp++)
355 continue;
356 /* Set cp to point to the remainder of the addresses. */
357 if (*qp == ',')
358 *qp++ = 0;
359 for (cp = qp, qp--; qp > pp; qp--)
360 if (*qp != 0) {
361 if (isspace ((unsigned char) *qp))
362 *qp = 0;
363 else
364 break;
365 }
366
367 return pp;
368 }
369
370
371 static void
372 add_aka (struct aka *ak, char *pp)
373 {
374 struct adr *ad, *ld;
375
376 for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next)
377 if (!strcmp (pp, ad->ad_text))
378 return;
379
380 NEW(ad);
381 ad->ad_text = mh_xstrdup(pp);
382 ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL;
383 ad->ad_next = NULL;
384 if (ak->ak_addr)
385 ld->ad_next = ad;
386 else
387 ak->ak_addr = ad;
388 }
389
390
391 static struct aka *
392 akalloc (char *id)
393 {
394 struct aka *p;
395
396 NEW(p);
397 p->ak_name = getcpy (id);
398 p->ak_visible = false;
399 p->ak_addr = NULL;
400 p->ak_next = NULL;
401 if (akatail != NULL)
402 akatail->ak_next = p;
403 if (akahead == NULL)
404 akahead = p;
405 akatail = p;
406
407 return p;
408 }