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