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