42742d296b3debe590a7ef61e894556ede7a324a
[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 <h/mh.h>
10 #include <h/addrsbr.h>
11 #include <h/mf.h>
12
13 /*
14 ** High level parsing of addresses:
15 **
16 ** The routines in sbr/mf.c parse the syntactic representations of
17 ** addresses.  The routines in sbr/addrsbr.c associate semantics with those
18 ** addresses.
19 **
20 ** If #ifdef DUMB is in effect, a full 822-style parser is called
21 ** for syntax recongition.  This breaks each address into its components.
22 ** Note however that no semantics are assumed about the parts or their
23 ** totality.  This means that implicit hostnames aren't made explicit,
24 ** and explicit hostnames aren't expanded to their "official" represenations.
25 **
26 ** If DUMB is not in effect, then this module does some
27 ** high-level thinking about what the addresses are.
28 **
29 ** 1. for MMDF systems:
30 **
31 **      string%<uucp>@<local>  ->  string
32 **
33 ** 2. for non-MMDF systems:
34 **
35 **      string@host.<uucp>  ->  host!string
36 **
37 ** 3. for any system, an address interpreted relative to the local host:
38 **
39 **      string@<uucp>  ->  string
40 **
41 ** For cases (1) and (3) above, the leftmost host is extracted.  If it's not
42 ** present, the local host is used.  If the tests above fail, the address is
43 ** considered to be a real 822-style address.
44 **
45 ** If an explicit host is not present, then MH checks for a bang to indicate
46 ** an explicit UUCP-style address.  If so, this is noted.  If not, the host is
47 ** defaulted, typically to the local host.  The lack of an explict host is
48 ** also noted.
49 **
50 ** If an explicit 822-style host is present, then MH checks to see if it
51 ** can expand this to the official name for the host.  If the hostname is
52 ** unknown, the address is so typed.
53 **
54 ** To summarize, when we're all done, here's what MH knows about the address:
55 **
56 ** DUMB - type: local, uucp, or network
57 **              host:  not locally defaulted, not explicitly expanded
58 **              everything else
59 **
60 ** other - type: local, uucp, network, unknown
61 **               everything else
62 */
63
64
65 static int  ingrp = 0;
66 static char *pers = NULL;
67 static char *mbox = NULL;
68 static char *host = NULL;
69 static char *route = NULL;
70 static char *grp = NULL;
71 static char *note = NULL;
72 static char err[BUFSIZ];
73 static char adr[BUFSIZ];
74
75 /* static prototype */
76 char *OfficialName(char *);
77
78
79 char *
80 getname(char *addrs)
81 {
82         struct adrx *ap;
83
84         pers = mbox = host = route = grp = note = NULL;
85         err[0] = '\0';
86
87         if ((ap = getadrx(addrs ? addrs : "")) == NULL)
88                 return NULL;
89
90         strncpy(adr, ap->text, sizeof(adr));
91         pers = ap->pers;
92         mbox = ap->mbox;
93         host = ap->host;
94         route = ap->path;
95         grp = ap->grp;
96         ingrp = ap->ingrp;
97         note = ap->note;
98         if (ap->err && *ap->err)
99                 strncpy(err, ap->err, sizeof(err));
100
101         return adr;
102 }
103
104
105 struct mailname *
106 getm(char *str, char *dfhost, int dftype, int wanthost, char *eresult)
107 {
108         char *pp;
109         struct mailname *mp;
110 #ifndef DUMB
111         char *dp;
112 #endif /* not DUMB */
113
114         if (err[0]) {
115                 if (eresult)
116                         strcpy(eresult, err);
117                 else
118                         if (wanthost == AD_HOST)
119                                 admonish(NULL, "bad address '%s' - %s", str, err);
120                 return NULL;
121         }
122         if (pers == NULL && mbox == NULL && host == NULL && route == NULL
123                 && grp == NULL) {
124                 if (eresult)
125                         strcpy(eresult, "null address");
126                 else
127                         if (wanthost == AD_HOST)
128                                 admonish(NULL, "null address '%s'", str);
129                 return NULL;
130         }
131         if (mbox == NULL && grp == NULL) {
132                 if (eresult)
133                         strcpy(eresult, "no mailbox in address");
134                 else if (wanthost == AD_HOST)
135                         admonish(NULL, "no mailbox in address '%s'", str);
136                 return NULL;
137         }
138
139         if (dfhost == NULL) {
140                 dfhost = LocalName();
141                 dftype = LOCALHOST;
142         }
143
144         mp = (struct mailname *) calloc((size_t) 1, sizeof(*mp));
145         if (mp == NULL) {
146                 if (eresult)
147                         strcpy(eresult, "insufficient memory to represent address");
148                 else if (wanthost == AD_HOST)
149                         adios(NULL, "insufficient memory to represent address");
150                 return NULL;
151         }
152
153         mp->m_next = NULL;
154         mp->m_text = getcpy(str);
155         if (pers)
156                 mp->m_pers = getcpy(pers);
157
158         if (mbox == NULL) {
159                 mp->m_type = BADHOST;
160                 mp->m_nohost = 1;
161                 mp->m_ingrp = ingrp;
162                 mp->m_gname = getcpy(grp);
163                 if (note)
164                         mp->m_note = getcpy(note);
165                 return mp;
166         }
167
168         if (host) {
169                 mp->m_mbox = getcpy(mbox);
170                 mp->m_host = getcpy(host);
171         } else {
172                 if ((pp = strchr(mbox, '!'))) {
173                         *pp++ = '\0';
174                         mp->m_mbox = getcpy(pp);
175                         mp->m_host = getcpy(mbox);
176                         mp->m_type = UUCPHOST;
177                 } else {
178                         mp->m_nohost = 1;
179                         mp->m_mbox = getcpy(mbox);
180 #ifdef DUMB
181                         if (route == NULL && dftype == LOCALHOST) {
182                                 mp->m_host = NULL;
183                                 mp->m_type = dftype;
184                         } else
185 #endif /* DUMB */
186                         {
187                                 mp->m_host = route ? NULL : getcpy(dfhost);
188                                 mp->m_type = route ? NETHOST : dftype;
189                         }
190                 }
191                 goto got_host;
192         }
193
194         if (wanthost == AD_NHST)
195                 mp->m_type = !mh_strcasecmp(LocalName(), mp->m_host)
196                         ? LOCALHOST : NETHOST;
197 #ifdef DUMB
198         else
199                 mp->m_type = mh_strcasecmp(LocalName(), mp->m_host) ?  NETHOST : LOCALHOST;
200 #else /* not DUMB */
201         else
202                 if (pp = OfficialName(mp->m_host)) {
203         got_real_host: ;
204                         free (mp->m_host);
205                         mp->m_host = getcpy(pp);
206                         mp->m_type = mh_strcasecmp(LocalName(), mp->m_host) ? NETHOST : LOCALHOST;
207                 } else {
208                         if (dp = strchr(mp->m_host, '.')) {
209                                 *dp = NULL;
210                                 if (pp = OfficialName(mp->m_host))
211                                         goto got_real_host;
212                                 *dp = '.';
213                         }
214                         mp->m_type = BADHOST;
215                 }
216 #endif /* not DUMB */
217
218 got_host: ;
219         if (route)
220                 mp->m_path = getcpy(route);
221         mp->m_ingrp = ingrp;
222         if (grp)
223                 mp->m_gname = getcpy(grp);
224         if (note)
225                 mp->m_note = getcpy(note);
226
227         return mp;
228 }
229
230
231 void
232 mnfree(struct mailname *mp)
233 {
234         if (!mp)
235                 return;
236
237         if (mp->m_text)
238                 free(mp->m_text);
239         if (mp->m_pers)
240                 free(mp->m_pers);
241         if (mp->m_mbox)
242                 free(mp->m_mbox);
243         if (mp->m_host)
244                 free(mp->m_host);
245         if (mp->m_path)
246                 free(mp->m_path);
247         if (mp->m_gname)
248                 free(mp->m_gname);
249         if (mp->m_note)
250                 free(mp->m_note);
251
252         free((char *) mp);
253 }
254
255
256 #define empty(s) ((s) ? (s) : "")
257
258 char *
259 adrformat(struct mailname *mp)
260 {
261         static char addr[BUFSIZ];
262         static char buffer[BUFSIZ];
263
264 #ifdef DUMB
265         if (mp->m_nohost)
266                 strncpy(addr, mp->m_mbox ? mp->m_mbox : "", sizeof(addr));
267         else
268 #endif /* DUMB */
269
270 #ifndef BANG
271         if (mp->m_type != UUCPHOST)
272                 snprintf(addr, sizeof(addr), mp->m_host ? "%s%s@%s" : "%s%s",
273                         empty(mp->m_path), empty(mp->m_mbox), mp->m_host);
274         else
275 #endif /* not BANG */
276                 snprintf(addr, sizeof(addr), "%s!%s", mp->m_host, mp->m_mbox);
277
278         if (mp->m_pers || mp->m_path) {
279                 if (mp->m_note)
280                         snprintf(buffer, sizeof(buffer), "%s %s <%s>",
281                                 legal_person(mp->m_pers ? mp->m_pers : mp->m_mbox),
282                                 mp->m_note, addr);
283                 else
284                         snprintf(buffer, sizeof(buffer), "%s <%s>",
285                                 legal_person(mp->m_pers ? mp->m_pers : mp->m_mbox),
286                                 addr);
287         } else if (mp->m_note)
288                 snprintf(buffer, sizeof(buffer), "%s %s", addr, mp->m_note);
289         else
290                 strncpy(buffer, addr, sizeof(buffer));
291
292         return buffer;
293 }
294
295
296 #define W_NIL   0x0000
297 #define W_MBEG  0x0001
298 #define W_MEND  0x0002
299 #define W_MBOX  (W_MBEG | W_MEND)
300 #define W_HBEG  0x0004
301 #define W_HEND  0x0008
302 #define W_HOST  (W_HBEG | W_HEND)
303 #define WBITS   "\020\01MBEG\02MEND\03HBEG\04HEND"
304
305 /*
306 ** Check if this is my address
307 */
308
309 int
310 ismymbox(struct mailname *np)
311 {
312         int oops;
313         register int len, i;
314         register char *cp;
315         register char *pp;
316         char buffer[BUFSIZ];
317         struct mailname *mp;
318         static char *am = NULL;
319         static struct mailname mq={NULL};
320
321         /*
322         ** If this is the first call, initialize
323         ** list of alternate mailboxes.
324         */
325         if (am == NULL) {
326                 mq.m_next = NULL;
327                 mq.m_mbox = getusername();
328                 if ((am = context_find("alternate-mailboxes")) == NULL)
329                         am = getusername();
330                 else {
331                         mp = &mq;
332                         oops = 0;
333                         while ((cp = getname(am))) {
334                                 if ((mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL)) == NULL) {
335                                         admonish(NULL, "illegal address: %s", cp);
336                                         oops++;
337                                 } else {
338                                         mp = mp->m_next;
339                                         mp->m_type = W_NIL;
340                                         if (*mp->m_mbox == '*') {
341                                                 mp->m_type |= W_MBEG;
342                                                 mp->m_mbox++;
343                                         }
344                                         if (*(cp = mp->m_mbox + strlen(mp->m_mbox) - 1) == '*') {
345                                                 mp->m_type |= W_MEND;
346                                                 *cp = '\0';
347                                         }
348                                         if (mp->m_host) {
349                                                 if (*mp->m_host == '*') {
350                                                         mp->m_type |= W_HBEG;
351                                                         mp->m_host++;
352                                                 }
353                                                 if (*(cp = mp->m_host + strlen(mp->m_host) - 1) == '*') {
354                                                         mp->m_type |= W_HEND;
355                                                         *cp = '\0';
356                                                 }
357                                         }
358                                         if ((cp = getenv("MHWDEBUG")) && *cp)
359                                                 fprintf(stderr, "mbox=\"%s\" host=\"%s\" %s\n",
360                                                         mp->m_mbox, mp->m_host,
361                                                         snprintb(buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
362                                 }
363                         }
364                         if (oops)
365                                 advise(NULL, "please fix the profile entry %s",
366                                                 "alternate-mailboxes");
367                 }
368         }
369
370         if (np == NULL) /* XXX */
371                 return 0;
372
373         switch (np->m_type) {
374         case NETHOST:
375                 len = strlen(cp = LocalName());
376                 if (!uprf(np->m_host, cp) || np->m_host[len] != '.')
377                         break;
378                 goto local_test;
379
380         case UUCPHOST:
381                 if (mh_strcasecmp(np->m_host, SystemName()))
382                         break;  /* fall */
383         case LOCALHOST:
384 local_test: ;
385                 if (!mh_strcasecmp(np->m_mbox, mq.m_mbox))
386                         return 1;
387                 break;
388
389         default:
390                 break;
391         }
392
393         /*
394         ** Now scan through list of alternate
395         ** mailboxes, and check for a match.
396         */
397         for (mp = &mq; mp->m_next;) {
398                 mp = mp->m_next;
399                 if (!np->m_mbox)
400                         continue;
401                 if ((len = strlen(cp = np->m_mbox))
402                         < (i = strlen(pp = mp->m_mbox)))
403                         continue;
404                 switch (mp->m_type & W_MBOX) {
405                 case W_NIL:
406                         if (mh_strcasecmp(cp, pp))
407                                 continue;
408                         break;
409                 case W_MBEG:
410                         if (mh_strcasecmp(cp + len - i, pp))
411                                 continue;
412                         break;
413                 case W_MEND:
414                         if (!uprf(cp, pp))
415                                 continue;
416                         break;
417                 case W_MBEG | W_MEND:
418                         if (stringdex(pp, cp) < 0)
419                                 continue;
420                         break;
421                 }
422
423                 if (mp->m_nohost)
424                         return 1;
425                 if (np->m_host == NULL)
426                         continue;
427                 if ((len = strlen(cp = np->m_host))
428                         < (i = strlen(pp = mp->m_host)))
429                         continue;
430                 switch (mp->m_type & W_HOST) {
431                 case W_NIL:
432                         if (mh_strcasecmp(cp, pp))
433                                 continue;
434                         break;
435                 case W_HBEG:
436                         if (mh_strcasecmp (cp + len - i, pp))
437                                 continue;
438                         break;
439                 case W_HEND:
440                         if (!uprf(cp, pp))
441                                 continue;
442                         break;
443                 case W_HBEG | W_HEND:
444                         if (stringdex(pp, cp) < 0)
445                                 continue;
446                         break;
447                 }
448                 return 1;
449         }
450
451         return 0;
452 }
453
454
455 /*
456 ** Moved from hosts.c -- find out the official name of a host
457 */
458
459 /*
460 ** In the SendMail world, we really don't know what the valid
461 ** hosts are.  We could poke around in the sendmail.cf file, but
462 ** that still isn't a guarantee.  As a result, we'll say that
463 ** everything is a valid host, and let SendMail worry about it.
464 */
465
466 #include <netdb.h>
467
468
469 char *
470 OfficialName(char *name)
471 {
472         unsigned char *p;
473         char *q, site[BUFSIZ];
474         struct addrinfo hints, *res;
475
476         static char buffer[BUFSIZ];
477
478         for (p = name, q = site; *p && (q - site < sizeof(site) - 1); p++, q++)
479                 *q = isupper(*p) ? tolower(*p) : *p;
480         *q = '\0';
481         q = site;
482
483         if (!mh_strcasecmp(LocalName(), site))
484                 return LocalName();
485
486         memset(&hints, 0, sizeof(hints));
487         hints.ai_flags = AI_CANONNAME;
488         hints.ai_family = PF_UNSPEC;
489
490         if (getaddrinfo(q, NULL, &hints, &res) == 0) {
491                 strncpy(buffer, res->ai_canonname, sizeof(buffer));
492                 buffer[sizeof(buffer) - 1] = '\0';
493                 freeaddrinfo(res);
494                 return buffer;
495         }
496
497         strncpy(buffer, site, sizeof(buffer));
498         return buffer;
499 }