Don't remove the draft on SIGQUIT to allow debugging
[mmh] / sbr / addrsbr.c
1 /*
2 ** addrsbr.c -- parse addresses 822-style
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 <sysexits.h>
10 #include <h/mh.h>
11 #include <h/utils.h>
12 #include <h/addrsbr.h>
13 #include <h/mf.h>
14
15 /*
16 ** High level parsing of addresses:
17 **
18 ** The routines in sbr/mf.c parse the syntactic representations of
19 ** addresses.  The routines in sbr/addrsbr.c associate semantics with those
20 ** addresses.
21 **
22 ** A full 822-style parser is called for syntax recongition. This breaks
23 ** each address into its components. Note however that no semantics are
24 ** assumed about the parts or their totality. This means that implicit
25 ** hostnames aren't made explicit, and explicit hostnames aren't expanded
26 ** to their "official" represenations.
27 **
28 ** To summarize, when we're all done, here's what MH knows about the address:
29 **       type: local or network
30 **       host:  not locally defaulted, not explicitly expanded
31 **       everything else
32 */
33
34
35 static int  ingrp = 0;
36 static char *pers = NULL;
37 static char *mbox = NULL;
38 static char *host = NULL;
39 static char *route = NULL;
40 static char *grp = NULL;
41 static char *note = NULL;
42 static char err[BUFSIZ];
43 static char adr[BUFSIZ];
44
45
46 char *
47 getname(char *addrs)
48 {
49         struct adrx *ap;
50
51         pers = mbox = host = route = grp = note = NULL;
52         err[0] = '\0';
53
54         if ((ap = getadrx(addrs ? addrs : "")) == NULL)
55                 return NULL;
56
57         strncpy(adr, ap->text, sizeof(adr));
58         pers = ap->pers;
59         mbox = ap->mbox;
60         host = ap->host;
61         route = ap->path;
62         grp = ap->grp;
63         ingrp = ap->ingrp;
64         note = ap->note;
65         if (ap->err && *ap->err)
66                 strncpy(err, ap->err, sizeof(err));
67
68         return adr;
69 }
70
71
72 struct mailname *
73 getm(char *str, char *dfhost, int dftype, int wanthost, char *eresult)
74 {
75         struct mailname *mp;
76
77         if (err[0]) {
78                 if (eresult)
79                         strcpy(eresult, err);
80                 else
81                         if (wanthost == AD_HOST)
82                                 admonish(NULL, "bad address '%s' - %s", str, err);
83                 return NULL;
84         }
85         if (pers == NULL && mbox == NULL && host == NULL && route == NULL
86                 && grp == NULL) {
87                 if (eresult)
88                         strcpy(eresult, "null address");
89                 else
90                         if (wanthost == AD_HOST)
91                                 admonish(NULL, "null address '%s'", str);
92                 return NULL;
93         }
94         if (mbox == NULL && grp == NULL) {
95                 if (eresult)
96                         strcpy(eresult, "no mailbox in address");
97                 else if (wanthost == AD_HOST)
98                         admonish(NULL, "no mailbox in address '%s'", str);
99                 return NULL;
100         }
101
102         if (dfhost == NULL) {
103                 dfhost = LocalName();
104                 dftype = LOCALHOST;
105         }
106
107         mp = (struct mailname *) mh_xcalloc((size_t) 1, sizeof(*mp));
108         if (mp == NULL) {
109                 if (eresult)
110                         strcpy(eresult, "insufficient memory to represent address");
111                 else if (wanthost == AD_HOST)
112                         adios(EX_OSERR, NULL, "insufficient memory to represent address");
113                 return NULL;
114         }
115
116         mp->m_next = NULL;
117         mp->m_text = getcpy(str);
118         if (pers)
119                 mp->m_pers = getcpy(pers);
120
121         if (mbox == NULL) {
122                 mp->m_type = BADHOST;
123                 mp->m_nohost = 1;
124                 mp->m_ingrp = ingrp;
125                 mp->m_gname = getcpy(grp);
126                 if (note)
127                         mp->m_note = getcpy(note);
128                 return mp;
129         }
130
131         if (host) {
132                 mp->m_mbox = getcpy(mbox);
133                 mp->m_host = getcpy(host);
134         } else {
135                 mp->m_nohost = 1;
136                 mp->m_mbox = getcpy(mbox);
137                 if (route == NULL && dftype == LOCALHOST) {
138                         mp->m_host = NULL;
139                         mp->m_type = dftype;
140                 } else {
141                         mp->m_host = route ? NULL : getcpy(dfhost);
142                         mp->m_type = route ? NETHOST : dftype;
143                 }
144                 goto got_host;
145         }
146
147         if (wanthost == AD_NHST)
148                 mp->m_type = !mh_strcasecmp(LocalName(), mp->m_host)
149                         ? LOCALHOST : NETHOST;
150         else
151                 mp->m_type = mh_strcasecmp(LocalName(), mp->m_host) ?  NETHOST : LOCALHOST;
152
153 got_host: ;
154         if (route)
155                 mp->m_path = getcpy(route);
156         mp->m_ingrp = ingrp;
157         if (grp)
158                 mp->m_gname = getcpy(grp);
159         if (note)
160                 mp->m_note = getcpy(note);
161
162         return mp;
163 }
164
165
166 void
167 mnfree(struct mailname *mp)
168 {
169         if (!mp)
170                 return;
171
172         if (mp->m_text)
173                 free(mp->m_text);
174         if (mp->m_pers)
175                 free(mp->m_pers);
176         if (mp->m_mbox)
177                 free(mp->m_mbox);
178         if (mp->m_host)
179                 free(mp->m_host);
180         if (mp->m_path)
181                 free(mp->m_path);
182         if (mp->m_gname)
183                 free(mp->m_gname);
184         if (mp->m_note)
185                 free(mp->m_note);
186
187         free((char *) mp);
188 }
189
190
191 #define empty(s) ((s) ? (s) : "")
192
193 char *
194 adrformat(struct mailname *mp)
195 {
196         static char addr[BUFSIZ];
197         static char buffer[BUFSIZ];
198
199         if (mp->m_nohost)
200                 strncpy(addr, mp->m_mbox ? mp->m_mbox : "", sizeof(addr));
201         else
202                 snprintf(addr, sizeof(addr), mp->m_host ? "%s%s@%s" : "%s%s",
203                         empty(mp->m_path), empty(mp->m_mbox), mp->m_host);
204
205         if (mp->m_pers || mp->m_path) {
206                 if (mp->m_note)
207                         snprintf(buffer, sizeof(buffer), "%s %s <%s>",
208                                 legal_person(mp->m_pers ? mp->m_pers : mp->m_mbox),
209                                 mp->m_note, addr);
210                 else
211                         snprintf(buffer, sizeof(buffer), "%s <%s>",
212                                 legal_person(mp->m_pers ? mp->m_pers : mp->m_mbox),
213                                 addr);
214         } else if (mp->m_note)
215                 snprintf(buffer, sizeof(buffer), "%s %s", addr, mp->m_note);
216         else
217                 strncpy(buffer, addr, sizeof(buffer));
218
219         return buffer;
220 }
221
222
223 #define W_NIL   0x0000
224 #define W_MBEG  0x0001
225 #define W_MEND  0x0002
226 #define W_MBOX  (W_MBEG | W_MEND)
227 #define W_HBEG  0x0004
228 #define W_HEND  0x0008
229 #define W_HOST  (W_HBEG | W_HEND)
230 #define WBITS   "\020\01MBEG\02MEND\03HBEG\04HEND"
231
232 /*
233 ** Check if this is my address
234 */
235
236 int
237 ismymbox(struct mailname *np)
238 {
239         int oops;
240         int len, i;
241         char *cp;
242         char *pp;
243         char buffer[BUFSIZ];
244         struct mailname *mp;
245         static char *am = NULL;
246         static struct mailname mq;
247
248         /*
249         ** If this is the first call, initialize
250         ** list of alternate mailboxes.
251         */
252         if (am == NULL) {
253                 mq.m_next = NULL;
254                 mq.m_mbox = getusername();
255                 if ((am = context_find("alternate-mailboxes")) == NULL)
256                         am = getusername();
257                 else {
258                         mp = &mq;
259                         oops = 0;
260                         while ((cp = getname(am))) {
261                                 if ((mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL)) == NULL) {
262                                         admonish(NULL, "illegal address: %s", cp);
263                                         oops++;
264                                 } else {
265                                         mp = mp->m_next;
266                                         mp->m_type = W_NIL;
267                                         if (*mp->m_mbox == '*') {
268                                                 mp->m_type |= W_MBEG;
269                                                 mp->m_mbox++;
270                                         }
271                                         if (*(cp = mp->m_mbox + strlen(mp->m_mbox) - 1) == '*') {
272                                                 mp->m_type |= W_MEND;
273                                                 *cp = '\0';
274                                         }
275                                         if (mp->m_host) {
276                                                 if (*mp->m_host == '*') {
277                                                         mp->m_type |= W_HBEG;
278                                                         mp->m_host++;
279                                                 }
280                                                 if (*(cp = mp->m_host + strlen(mp->m_host) - 1) == '*') {
281                                                         mp->m_type |= W_HEND;
282                                                         *cp = '\0';
283                                                 }
284                                         }
285                                         if ((cp = getenv("MHWDEBUG")) && *cp)
286                                                 fprintf(stderr, "mbox=\"%s\" host=\"%s\" %s\n",
287                                                         mp->m_mbox, mp->m_host,
288                                                         snprintb(buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
289                                 }
290                         }
291                         if (oops)
292                                 advise(NULL, "please fix the profile entry %s",
293                                                 "alternate-mailboxes");
294                 }
295         }
296
297         if (np == NULL) /* XXX */
298                 return 0;
299
300         switch (np->m_type) {
301         case NETHOST:
302                 len = strlen(cp = LocalName());
303                 if (!uprf(np->m_host, cp) || np->m_host[len] != '.')
304                         break;
305                 goto local_test;
306
307         case LOCALHOST:
308 local_test: ;
309                 if (!mh_strcasecmp(np->m_mbox, mq.m_mbox))
310                         return 1;
311                 break;
312
313         default:
314                 break;
315         }
316
317         /*
318         ** Now scan through list of alternate
319         ** mailboxes, and check for a match.
320         */
321         for (mp = &mq; mp->m_next;) {
322                 mp = mp->m_next;
323                 if (!np->m_mbox)
324                         continue;
325                 if ((len = strlen(cp = np->m_mbox))
326                         < (i = strlen(pp = mp->m_mbox)))
327                         continue;
328                 switch (mp->m_type & W_MBOX) {
329                 case W_NIL:
330                         if (mh_strcasecmp(cp, pp))
331                                 continue;
332                         break;
333                 case W_MBEG:
334                         if (mh_strcasecmp(cp + len - i, pp))
335                                 continue;
336                         break;
337                 case W_MEND:
338                         if (!uprf(cp, pp))
339                                 continue;
340                         break;
341                 case W_MBEG | W_MEND:
342                         if (stringdex(pp, cp) < 0)
343                                 continue;
344                         break;
345                 }
346
347                 if (mp->m_nohost)
348                         return 1;
349                 if (np->m_host == NULL)
350                         continue;
351                 if ((len = strlen(cp = np->m_host))
352                         < (i = strlen(pp = mp->m_host)))
353                         continue;
354                 switch (mp->m_type & W_HOST) {
355                 case W_NIL:
356                         if (mh_strcasecmp(cp, pp))
357                                 continue;
358                         break;
359                 case W_HBEG:
360                         if (mh_strcasecmp (cp + len - i, pp))
361                                 continue;
362                         break;
363                 case W_HEND:
364                         if (!uprf(cp, pp))
365                                 continue;
366                         break;
367                 case W_HBEG | W_HEND:
368                         if (stringdex(pp, cp) < 0)
369                                 continue;
370                         break;
371                 }
372                 return 1;
373         }
374
375         return 0;
376 }
377
378 /*
379  * Insert mailname after element and returns the
380  * number of parsed addresses. element is set to
381  * the last parsed addresse.
382  */
383 size_t
384 getmboxes(char *line, struct mailname **element)
385 {
386         struct mailname *mp, *next;
387         char *cp;
388         size_t i = 0;
389
390         next = (*element)->m_next;
391
392         while ((cp = getname(line))) {
393                 mp = getm(cp, NULL, 0, AD_HOST, NULL);
394                 (*element)->m_next = mp;
395                 *element = mp;
396                 i++;
397         }
398
399         (*element)->m_next = next;
400         return i;
401 }