Removed the last masquerading type: username_extension.
[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
76 /*
77 ** external prototypes
78 */
79 char *getusername(void);
80
81
82 char *
83 getname(char *addrs)
84 {
85         struct adrx *ap;
86
87         pers = mbox = host = route = grp = note = NULL;
88         err[0] = '\0';
89
90         if ((ap = getadrx(addrs ? addrs : "")) == NULL)
91                 return NULL;
92
93         strncpy(adr, ap->text, sizeof(adr));
94         pers = ap->pers;
95         mbox = ap->mbox;
96         host = ap->host;
97         route = ap->path;
98         grp = ap->grp;
99         ingrp = ap->ingrp;
100         note = ap->note;
101         if (ap->err && *ap->err)
102                 strncpy(err, ap->err, sizeof(err));
103
104         return adr;
105 }
106
107
108 struct mailname *
109 getm(char *str, char *dfhost, int dftype, int wanthost, char *eresult)
110 {
111         char *pp;
112         struct mailname *mp;
113 #ifndef DUMB
114         char *dp;
115 #endif /* not DUMB */
116
117         if (err[0]) {
118                 if (eresult)
119                         strcpy(eresult, err);
120                 else
121                         if (wanthost == AD_HOST)
122                                 admonish(NULL, "bad address '%s' - %s", str, err);
123                 return NULL;
124         }
125         if (pers == NULL && mbox == NULL && host == NULL && route == NULL
126                 && grp == NULL) {
127                 if (eresult)
128                         strcpy(eresult, "null address");
129                 else
130                         if (wanthost == AD_HOST)
131                                 admonish(NULL, "null address '%s'", str);
132                 return NULL;
133         }
134         if (mbox == NULL && grp == NULL) {
135                 if (eresult)
136                         strcpy(eresult, "no mailbox in address");
137                 else if (wanthost == AD_HOST)
138                         admonish(NULL, "no mailbox in address '%s'", str);
139                 return NULL;
140         }
141
142         if (dfhost == NULL) {
143                 dfhost = LocalName();
144                 dftype = LOCALHOST;
145         }
146
147         mp = (struct mailname *) calloc((size_t) 1, sizeof(*mp));
148         if (mp == NULL) {
149                 if (eresult)
150                         strcpy(eresult, "insufficient memory to represent address");
151                 else if (wanthost == AD_HOST)
152                         adios(NULL, "insufficient memory to represent address");
153                 return NULL;
154         }
155
156         mp->m_next = NULL;
157         mp->m_text = getcpy(str);
158         if (pers)
159                 mp->m_pers = getcpy(pers);
160
161         if (mbox == NULL) {
162                 mp->m_type = BADHOST;
163                 mp->m_nohost = 1;
164                 mp->m_ingrp = ingrp;
165                 mp->m_gname = getcpy(grp);
166                 if (note)
167                         mp->m_note = getcpy(note);
168                 return mp;
169         }
170
171         if (host) {
172                 mp->m_mbox = getcpy(mbox);
173                 mp->m_host = getcpy(host);
174         } else {
175                 if ((pp = strchr(mbox, '!'))) {
176                         *pp++ = '\0';
177                         mp->m_mbox = getcpy(pp);
178                         mp->m_host = getcpy(mbox);
179                         mp->m_type = UUCPHOST;
180                 } else {
181                         mp->m_nohost = 1;
182                         mp->m_mbox = getcpy(mbox);
183 #ifdef DUMB
184                         if (route == NULL && dftype == LOCALHOST) {
185                                 mp->m_host = NULL;
186                                 mp->m_type = dftype;
187                         } else
188 #endif /* DUMB */
189                         {
190                                 mp->m_host = route ? NULL : getcpy(dfhost);
191                                 mp->m_type = route ? NETHOST : dftype;
192                         }
193                 }
194                 goto got_host;
195         }
196
197         if (wanthost == AD_NHST)
198                 mp->m_type = !mh_strcasecmp(LocalName(), mp->m_host)
199                         ? LOCALHOST : NETHOST;
200 #ifdef DUMB
201         else
202                 mp->m_type = mh_strcasecmp(LocalName(), mp->m_host) ?  NETHOST : LOCALHOST;
203 #else /* not DUMB */
204         else
205                 if (pp = OfficialName(mp->m_host)) {
206         got_real_host: ;
207                         free (mp->m_host);
208                         mp->m_host = getcpy(pp);
209                         mp->m_type = mh_strcasecmp(LocalName(), mp->m_host) ? NETHOST : LOCALHOST;
210                 } else {
211                         if (dp = strchr(mp->m_host, '.')) {
212                                 *dp = NULL;
213                                 if (pp = OfficialName(mp->m_host))
214                                         goto got_real_host;
215                                 *dp = '.';
216                         }
217                         mp->m_type = BADHOST;
218                 }
219 #endif /* not DUMB */
220
221 got_host: ;
222         if (route)
223                 mp->m_path = getcpy(route);
224         mp->m_ingrp = ingrp;
225         if (grp)
226                 mp->m_gname = getcpy(grp);
227         if (note)
228                 mp->m_note = getcpy(note);
229
230         return mp;
231 }
232
233
234 void
235 mnfree(struct mailname *mp)
236 {
237         if (!mp)
238                 return;
239
240         if (mp->m_text)
241                 free(mp->m_text);
242         if (mp->m_pers)
243                 free(mp->m_pers);
244         if (mp->m_mbox)
245                 free(mp->m_mbox);
246         if (mp->m_host)
247                 free(mp->m_host);
248         if (mp->m_path)
249                 free(mp->m_path);
250         if (mp->m_gname)
251                 free(mp->m_gname);
252         if (mp->m_note)
253                 free(mp->m_note);
254
255         free((char *) mp);
256 }
257
258
259 #define empty(s) ((s) ? (s) : "")
260
261 char *
262 adrformat(struct mailname *mp)
263 {
264         static char addr[BUFSIZ];
265         static char buffer[BUFSIZ];
266
267 #ifdef DUMB
268         if (mp->m_nohost)
269                 strncpy(addr, mp->m_mbox ? mp->m_mbox : "", sizeof(addr));
270         else
271 #endif /* DUMB */
272
273 #ifndef BANG
274         if (mp->m_type != UUCPHOST)
275                 snprintf(addr, sizeof(addr), mp->m_host ? "%s%s@%s" : "%s%s",
276                         empty(mp->m_path), empty(mp->m_mbox), mp->m_host);
277         else
278 #endif /* not BANG */
279                 snprintf(addr, sizeof(addr), "%s!%s", mp->m_host, mp->m_mbox);
280
281         if (mp->m_pers || mp->m_path) {
282                 if (mp->m_note)
283                         snprintf(buffer, sizeof(buffer), "%s %s <%s>",
284                                 legal_person(mp->m_pers ? mp->m_pers : mp->m_mbox),
285                                 mp->m_note, addr);
286                 else
287                         snprintf(buffer, sizeof(buffer), "%s <%s>",
288                                 legal_person(mp->m_pers ? mp->m_pers : mp->m_mbox),
289                                 addr);
290         } else if (mp->m_note)
291                 snprintf(buffer, sizeof(buffer), "%s %s", addr, mp->m_note);
292         else
293                 strncpy(buffer, addr, sizeof(buffer));
294
295         return buffer;
296 }
297
298
299 #define W_NIL   0x0000
300 #define W_MBEG  0x0001
301 #define W_MEND  0x0002
302 #define W_MBOX  (W_MBEG | W_MEND)
303 #define W_HBEG  0x0004
304 #define W_HEND  0x0008
305 #define W_HOST  (W_HBEG | W_HEND)
306 #define WBITS   "\020\01MBEG\02MEND\03HBEG\04HEND"
307
308 /*
309 ** Check if this is my address
310 */
311
312 int
313 ismymbox(struct mailname *np)
314 {
315         int oops;
316         register int len, i;
317         register char *cp;
318         register char *pp;
319         char buffer[BUFSIZ];
320         struct mailname *mp;
321         static char *am = NULL;
322         static struct mailname mq={NULL};
323
324         /*
325         ** If this is the first call, initialize
326         ** list of alternate mailboxes.
327         */
328         if (am == NULL) {
329                 mq.m_next = NULL;
330                 mq.m_mbox = getusername();
331                 if ((am = context_find("alternate-mailboxes")) == NULL)
332                         am = getusername();
333                 else {
334                         mp = &mq;
335                         oops = 0;
336                         while ((cp = getname(am))) {
337                                 if ((mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL)) == NULL) {
338                                         admonish(NULL, "illegal address: %s", cp);
339                                         oops++;
340                                 } else {
341                                         mp = mp->m_next;
342                                         mp->m_type = W_NIL;
343                                         if (*mp->m_mbox == '*') {
344                                                 mp->m_type |= W_MBEG;
345                                                 mp->m_mbox++;
346                                         }
347                                         if (*(cp = mp->m_mbox + strlen(mp->m_mbox) - 1) == '*') {
348                                                 mp->m_type |= W_MEND;
349                                                 *cp = '\0';
350                                         }
351                                         if (mp->m_host) {
352                                                 if (*mp->m_host == '*') {
353                                                         mp->m_type |= W_HBEG;
354                                                         mp->m_host++;
355                                                 }
356                                                 if (*(cp = mp->m_host + strlen(mp->m_host) - 1) == '*') {
357                                                         mp->m_type |= W_HEND;
358                                                         *cp = '\0';
359                                                 }
360                                         }
361                                         if ((cp = getenv("MHWDEBUG")) && *cp)
362                                                 fprintf(stderr, "mbox=\"%s\" host=\"%s\" %s\n",
363                                                         mp->m_mbox, mp->m_host,
364                                                         snprintb(buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
365                                 }
366                         }
367                         if (oops)
368                                 advise(NULL, "please fix the profile entry %s",
369                                                 "alternate-mailboxes");
370                 }
371         }
372
373         if (np == NULL) /* XXX */
374                 return 0;
375
376         switch (np->m_type) {
377         case NETHOST:
378                 len = strlen(cp = LocalName());
379                 if (!uprf(np->m_host, cp) || np->m_host[len] != '.')
380                         break;
381                 goto local_test;
382
383         case UUCPHOST:
384                 if (mh_strcasecmp(np->m_host, SystemName()))
385                         break;  /* fall */
386         case LOCALHOST:
387 local_test: ;
388                 if (!mh_strcasecmp(np->m_mbox, mq.m_mbox))
389                         return 1;
390                 break;
391
392         default:
393                 break;
394         }
395
396         /*
397         ** Now scan through list of alternate
398         ** mailboxes, and check for a match.
399         */
400         for (mp = &mq; mp->m_next;) {
401                 mp = mp->m_next;
402                 if (!np->m_mbox)
403                         continue;
404                 if ((len = strlen(cp = np->m_mbox))
405                         < (i = strlen(pp = mp->m_mbox)))
406                         continue;
407                 switch (mp->m_type & W_MBOX) {
408                 case W_NIL:
409                         if (mh_strcasecmp(cp, pp))
410                                 continue;
411                         break;
412                 case W_MBEG:
413                         if (mh_strcasecmp(cp + len - i, pp))
414                                 continue;
415                         break;
416                 case W_MEND:
417                         if (!uprf(cp, pp))
418                                 continue;
419                         break;
420                 case W_MBEG | W_MEND:
421                         if (stringdex(pp, cp) < 0)
422                                 continue;
423                         break;
424                 }
425
426                 if (mp->m_nohost)
427                         return 1;
428                 if (np->m_host == NULL)
429                         continue;
430                 if ((len = strlen(cp = np->m_host))
431                         < (i = strlen(pp = mp->m_host)))
432                         continue;
433                 switch (mp->m_type & W_HOST) {
434                 case W_NIL:
435                         if (mh_strcasecmp(cp, pp))
436                                 continue;
437                         break;
438                 case W_HBEG:
439                         if (mh_strcasecmp (cp + len - i, pp))
440                                 continue;
441                         break;
442                 case W_HEND:
443                         if (!uprf(cp, pp))
444                                 continue;
445                         break;
446                 case W_HBEG | W_HEND:
447                         if (stringdex(pp, cp) < 0)
448                                 continue;
449                         break;
450                 }
451                 return 1;
452         }
453
454         return 0;
455 }
456
457
458 /*
459 ** Moved from hosts.c -- find out the official name of a host
460 */
461
462 /*
463 ** In the SendMail world, we really don't know what the valid
464 ** hosts are.  We could poke around in the sendmail.cf file, but
465 ** that still isn't a guarantee.  As a result, we'll say that
466 ** everything is a valid host, and let SendMail worry about it.
467 */
468
469 #include <h/mts.h>
470 #include <netdb.h>
471
472
473 char *
474 OfficialName(char *name)
475 {
476         unsigned char *p;
477         char *q, site[BUFSIZ];
478         struct addrinfo hints, *res;
479
480         static char buffer[BUFSIZ];
481
482         for (p = name, q = site; *p && (q - site < sizeof(site) - 1); p++, q++)
483                 *q = isupper(*p) ? tolower(*p) : *p;
484         *q = '\0';
485         q = site;
486
487         if (!mh_strcasecmp(LocalName(), site))
488                 return LocalName();
489
490         memset(&hints, 0, sizeof(hints));
491         hints.ai_flags = AI_CANONNAME;
492         hints.ai_family = PF_UNSPEC;
493
494         if (getaddrinfo(q, NULL, &hints, &res) == 0) {
495                 strncpy(buffer, res->ai_canonname, sizeof(buffer));
496                 buffer[sizeof(buffer) - 1] = '\0';
497                 freeaddrinfo(res);
498                 return buffer;
499         }
500
501         strncpy(buffer, site, sizeof(buffer));
502         return buffer;
503 }