1 /* addrsbr.c - parse addresses 822-style */
3 static char ident[] = "@(#)$Id: addrsbr.c,v 1.13 1992/10/26 22:44:26 jromine Exp $";
7 #include "../h/addrsbr.h"
8 #include "../zotnet/mf.h"
14 /* High level parsing of addresses:
16 The routines in zotnet/mf/mf.c parse the syntactic representations of
17 addresses. The routines in sbr/addrsbr.c associate semantics with those
20 If #ifdef BERK is in effect, the routines in mf.c aren't called and only
21 the most rudimentary syntax parse is done. The parse is not 822-conformant.
22 This causes problems as there is no semantics associated with the address
23 at all--it's just a string. (the author of the BERK code disagrees with
24 the preceding, of course. BERK solves problems for incoming mail
25 because it will accept damn near any address. BERK was intended to be
26 used when spost is the interface to the mail delivery system which means
27 all outgoing address interpretation is left to sendmail. It is possible,
28 though unlikely, for BERK address parsing to interact poorly with
29 "post". - van@monet.berkeley.edu).
31 Instead, if #ifdef DUMB is in effect, a full 822-style parser is called
32 for syntax recongition. This breaks each address into its components.
33 Note however that no semantics are assumed about the parts or their
34 totality. This means that implicit hostnames aren't made explicit,
35 and explicit hostnames aren't expanded to their "official" represenations.
37 If neither BERK nor DUMB is in effect, then this module does some
38 high-level thinking about what the addresses are. If #ifdef MF is in
39 effect, then MH will deduce UUCP-style addressing masquerading as
44 string%<uucp>@<local> -> string
46 2. for non-MMDF systems:
48 string@host.<uucp> -> host!string
50 3. for any system, an address interpreted relative to the local host:
52 string@<uucp> -> string
54 For cases (1) and (3) above, the leftmost host is extracted. If it's not
55 present, the local host is used. If #ifdef MF is not in effect or the
56 tests above fail, the address is considered to be a real 822-style address.
58 If an explicit host is not present, then MH checks for a bang to indicate
59 an explicit UUCP-style address. If so, this is noted. If not, the host is
60 defaulted, typically to the local host. The lack of an explict host is
63 If an explicit 822-style host is present, then MH checks to see if it
64 can expand this to the official name for the host. If the hostname is
65 unknown, the address is so typed.
67 To summarize, when we're all done, here's what MH knows about the address:
70 nohost: set if no '@' or '!' in mailbox
71 text: exact copy of address
72 mbox: lowercase version of mailbox
74 DUMB - type: local, uucp, or network
75 host: not locally defaulted, not explicitly expanded
78 other - type: local, uucp, network, unknown
84 #if !defined(DUMB) && defined(SENDMTS) && !defined(BANG)
86 #define UucpChan() "UUCP"
90 static char *err = NULL;
91 static char adrtext[BUFSIZ];
95 static char *pers = NULL;
96 static char *mbox = NULL;
97 static char *host = NULL;
98 static char *route = NULL;
99 static char *grp = NULL;
100 static char *note = NULL;
102 static char err[BUFSIZ];
103 #endif /* not BERK */
104 static char adr[BUFSIZ];
111 char *getname (addrs)
112 register char *addrs;
116 * Berkeley uses a very simple parser since Sendmail does all the work.
117 * The only thing that does address parsing if BERK is defined is the
118 * routine "ismybox" used by "scan" & "repl" to identify the current
121 * This routine does essentially the same address interpretation as the
122 * routine "prescan" in "sendmail". The intent is that MH should
123 * make minimum assumptions about address forms since it doesn't
124 * have access to the information in the sendmail config file
125 * (God forbid that anything but sendmail has to deal with a sendmail
126 * config file) and, therefore, hasn't the faintest idea of what will
127 * or won't be a legal address.
129 * Since this parse is only used by "ismybox" and repl, it just does
130 * two things: split multiple addr on a line into separate addresses and
131 * locate the "mailbox" portion of an address. The parse uses rfc-822
132 * metacharacters and quoting but is much less restrictive that rfc-822.
133 * In detail, `,' or eos terminate addresses. "Empty" addresses
134 * (e.g., `,,') are ignored. Double quote ("), backslash, left & right
135 * paren and left and right angle brackets are metacharacters. Left &
136 * right parens must balance as must left & right angle brackets. Any
137 * metacharacter may be escaped by preceding it with a backslash.
138 * Any text between parens is considered a comment and ignored (i.e.,
139 * only `(', `)' and `\' are metacharacters following a `('). Text
140 * between double quotes is considered escaped (i.e., only `"' and
141 * `\' are metacharacters following a `"'). The `mailbox' portion
142 * of an address is the non-comment text between angle-brackets if
143 * the address contains any angle brackets. Otherwise, it is all the
144 * non-comment text. Blanks, tabs & newlines will not be included
145 * in the mailbox portion of an address unless they are escaped.
149 #define NORMAL (0<<8)
150 #define QS (1<<8) /* in quoted string */
151 #define COM (2<<8) /* in comment (...) */
152 #define ERR (3<<8) /* found an error */
153 #define EOA (4<<8) /* end of address */
155 static char *saved_addr = NULL; /* saved address line ptr */
156 static char *adr_ptr = NULL; /* where to start looking for
157 next address on line */
158 register char *nxtout = adr; /* where to put next character of
159 `mailbox' part of address */
161 register int state = NORMAL;
162 register char *adrcopy = adrtext; /* where to put next character of
164 register int lbcnt = 0; /* number of unmatched "(" */
165 register int lpcnt = 0; /* number of unmatched "<" */
175 addrs = saved_addr = getcpy(addrs ? addrs : "");
177 /* skip any leading whitespace or commas. */
178 while ( (c = *addrs++) == ',' || isspace(c))
181 *nxtout = *adrcopy = '\0';
182 while (state != EOA) {
185 *nxtout++ = (isalpha(c) && isupper (c)) ? tolower (c) : c;
188 case NORMAL+'\n': /* discard newlines */
196 case NORMAL+' ': /* skip unquoted whitespace */
201 case NORMAL+'"': /* start quoted string */
205 case QS+'"': /* end quoted string */
210 nxtout = adr; /* start over accumulating address */
235 } else if (lpcnt == 0)
242 if ((c = *addrs++) == '\n' || c == '\0') {
247 *nxtout++ = (isalpha(c) && isupper (c)) ? tolower (c) : c;
275 * at this point adr contains the `mailbox' part of the address
276 * in lower case & minus any comment or unquoted whitespace.
277 * adrtext contains an exact copy of the address and
278 * addr points to where we should start scanning next time.
280 *(nxtout-1) = *(adrcopy-1) = '\0';
290 register struct adrx *ap;
292 pers = mbox = host = route = grp = note = NULL;
295 if ((ap = getadrx (addrs ? addrs : "")) == NULL)
298 (void) strcpy (adr, ap -> text);
306 if (ap -> err && *ap -> err)
307 (void) strcpy (err, ap -> err);
310 #endif /* not BERK */
319 struct mailname *getm (str, dfhost, dftype, wanthost, eresult)
330 #endif /* not DUMB */
332 char *up = UucpChan ();
334 #endif /* not BERK */
335 register struct mailname *mp;
339 (void) strcpy (eresult, err);
341 if (wanthost == AD_HOST)
342 admonish (NULLCP, "bad address '%s' - %s", str, err);
346 if (str == NULL || *str == '\0') {
349 && mbox == NULL && host == NULL && route == NULL
351 #endif /* not BERK */
353 (void) strcpy (eresult, "null address");
355 if (wanthost == AD_HOST)
356 admonish (NULLCP, "null address '%s'", str);
360 if (mbox == NULL && grp == NULL) {
362 (void) strcpy (eresult, "no mailbox in address");
364 if (wanthost == AD_HOST)
365 admonish (NULLCP, "no mailbox in address '%s'", str);
369 if (dfhost == NULL) {
370 dfhost = LocalName ();
373 #endif /* not BERK */
375 mp = (struct mailname *) calloc ((unsigned) 1, sizeof *mp);
378 (void) strcpy (eresult, "insufficient memory to represent address");
380 if (wanthost == AD_HOST)
381 adios (NULLCP, "insufficient memory to represent address");
386 mp -> m_text = getcpy (str);
388 mp -> m_type = LOCALHOST;
389 mp -> m_mbox = getcpy (adr);
390 if (!index (adr, '@') && !index (adr, '!'))
394 mp -> m_pers = getcpy (pers);
397 mp -> m_type = BADHOST;
399 mp -> m_ingrp = ingrp;
400 mp -> m_gname = getcpy (grp);
402 mp -> m_note = getcpy (note);
411 if (up && uleq (host, LocalName ())
412 && (pp = rindex (mbox, '%'))
413 && uleq (up, pp + 1)) {/* uucpaddr%<uucp>@<local> */
417 #else /* not MMDFMTS */
418 if (up && (pp = index (host, '.'))
419 && uleq (up, pp + 1)) {/* uucpaddr@host.<uucp> */
421 mp -> m_host = getcpy (host);
422 mp -> m_mbox = getcpy (mbox);
423 mp -> m_type = UUCPHOST;
426 #endif /* not MMDFMTS */
427 if (up && uleq (dfhost, LocalName ())
428 && uleq (up, host)) {/* uucpaddr@<uucp> [local] */
429 if (pp = index (mbox, '!')) {
431 mp -> m_host = getcpy (mbox);
432 mp -> m_mbox = getcpy (pp);
435 mp -> m_host = getcpy (SystemName ());
436 mp -> m_mbox = getcpy (mbox);
438 mp -> m_type = UUCPHOST;
442 mp -> m_mbox = getcpy (mbox);
443 mp -> m_host = getcpy (host);
446 if (pp = index (mbox, '!')) {
448 mp -> m_mbox = getcpy (pp);
449 mp -> m_host = getcpy (mbox);
450 mp -> m_type = UUCPHOST;
454 mp -> m_mbox = getcpy (mbox);
456 if (route == NULL && dftype == LOCALHOST) {
457 mp -> m_host = NULLCP;
458 mp -> m_type = dftype;
463 mp -> m_host = route ? NULLCP : getcpy (dfhost);
464 mp -> m_type = route ? NETHOST : dftype;
472 if (wanthost == AD_NHST)
473 mp -> m_type = uleq (LocalName (), mp -> m_host)
474 ? LOCALHOST : NETHOST;
477 mp -> m_type = uleq (LocalName (), mp -> m_host) ? LOCALHOST
481 if (pp = OfficialName (mp -> m_host)) {
484 mp -> m_host = getcpy (pp);
485 mp -> m_type = uleq (LocalName (), mp -> m_host) ? LOCALHOST
489 if (dp = index (mp -> m_host, '.')) {
491 if (pp = OfficialName (mp -> m_host))
495 mp -> m_type = BADHOST;
497 #endif /* not DUMB */
501 mp -> m_path = getcpy (route);
502 mp -> m_ingrp = ingrp;
504 mp -> m_gname = getcpy (grp);
506 mp -> m_note = getcpy (note);
507 #endif /* not BERK */
515 register struct mailname *mp;
531 free (mp -> m_gname);
544 char *auxformat (mp, extras)
545 register struct mailname *mp;
550 char *up = UucpChan ();
552 static char addr[BUFSIZ];
553 #endif /* not BERK */
554 static char buffer[BUFSIZ];
557 /* this "if" is a crufty hack to handle "visible" aliases */
558 if (mp->m_pers && !extras)
559 (void) sprintf (buffer, "%s <%s>", mp->m_pers, mp->m_mbox);
561 (void) strcpy (buffer, mp -> m_text);
565 if (up && mp -> m_type == UUCPHOST)
567 (void) sprintf (addr, "%s!%s%%%s@%s", mp -> m_host, mp -> m_mbox,
569 #else /* not MMDFMTS */
570 (void) sprintf (addr, "%s@%s.%s", mp -> m_mbox, mp -> m_host, up);
571 #endif /* not MMDFMTS */
577 (void) strcpy (addr, mp -> m_mbox ? mp -> m_mbox : "");
582 if (mp -> m_type != UUCPHOST)
583 (void) sprintf (addr, mp -> m_host ? "%s%s@%s" : "%s%s",
584 mp -> m_path ? mp -> m_path : "", mp -> m_mbox, mp -> m_host);
586 #endif /* not BANG */
587 (void) sprintf (addr, "%s!%s", mp -> m_host, mp -> m_mbox);
592 if (mp -> m_pers || mp -> m_path)
594 (void) sprintf (buffer, "%s %s <%s>",
595 legal_person (mp -> m_pers ? mp -> m_pers : mp -> m_mbox),
598 (void) sprintf (buffer, "%s <%s>",
599 legal_person (mp -> m_pers ? mp -> m_pers : mp -> m_mbox),
603 (void) sprintf (buffer, "%s %s", addr, mp -> m_note);
605 (void) strcpy (buffer, addr);
606 #endif /* not BERK */
613 #if defined(BERK) || (defined(DUMB) && !defined(MMDFMTS) && !defined(SMTP))
619 char *adrsprintf (local, domain)
623 static char addr[BUFSIZ];
629 #endif /* REALLYDUMB */
636 #endif /* REALLYDUMB */
637 domain = LocalName ();
640 (void) sprintf (addr, "%s@%s", local, domain);
642 (void) sprintf (addr, "%s!%s", domain, local);
651 #define W_MBEG 0x0001
652 #define W_MEND 0x0002
653 #define W_MBOX (W_MBEG | W_MEND)
654 #define W_HBEG 0x0004
655 #define W_HEND 0x0008
656 #define W_HOST (W_HBEG | W_HEND)
657 #define WBITS "\020\01MBEG\02MEND\03HBEG\04HEND"
661 register struct mailname *np;
670 #endif /* not BERK */
671 register struct mailname *mp;
672 static char *am = NULL;
673 static struct mailname mq={NULL};
675 /* if this is the first call, init. alternate mailboxes list */
678 mq.m_mbox = getusr ();
679 if ((am = m_find ("alternate-mailboxes")) == NULL)
684 while (cp = getname (am))
685 if ((mp -> m_next = getm (cp, NULLCP, 0, AD_NAME, NULLCP))
687 admonish (NULLCP, "illegal address: %s", cp), oops++;
690 mp -> m_type = W_NIL;
692 /* check for wildcards on the mailbox name and
693 set m_type accordingly. */
694 mp -> m_ingrp = strlen (mp -> m_mbox);
695 if (*(mp -> m_mbox) == '*') {
696 mp -> m_type |= W_MBEG;
700 if (mp -> m_mbox[mp -> m_ingrp - 1] == '*') {
701 mp -> m_type |= W_MEND;
703 mp -> m_mbox[mp -> m_ingrp] = 0;
706 /* owing to screwy munging, wildcarding is a great idea
707 even under #ifndef BERK, so... */
708 mp -> m_type = W_NIL;
709 if (*mp -> m_mbox == '*')
710 mp -> m_type |= W_MBEG, mp -> m_mbox++;
711 if (*(cp = mp -> m_mbox + strlen (mp -> m_mbox) - 1)
713 mp -> m_type |= W_MEND, *cp = '\0';
715 if (*mp -> m_host == '*')
716 mp -> m_type |= W_HBEG, mp -> m_host++;
717 if (*(cp = mp -> m_host + strlen (mp -> m_host) - 1)
719 mp -> m_type |= W_HEND, *cp = '\0';
721 if ((cp = getenv ("MHWDEBUG")) && *cp)
722 fprintf (stderr, "mbox=\"%s\" host=\"%s\" %s\n",
723 mp -> m_mbox, mp -> m_host,
724 sprintb (buffer, (unsigned) mp -> m_type, WBITS));
725 #endif /* not BERK */
728 advise (NULLCP, "please fix the %s: entry in your %s file",
729 "alternate-mailboxes", mh_profile);
732 if (np == NULL) /* XXX */
737 if (strcmp (cp, mq.m_mbox) == 0)
740 switch (np -> m_type) {
742 len = strlen (cp = LocalName ());
743 if (!uprf (np -> m_host, cp) || np -> m_host[len] != '.')
748 if (!uleq (np -> m_host, SystemName ()))
752 if (uleq (np -> m_mbox, mq.m_mbox))
759 #endif /* not BERK */
763 for (mp = &mq; mp = mp -> m_next;)
764 if (len >= mp -> m_ingrp)
765 switch (mp -> m_type) {
766 case W_NIL: /* no wildcards */
767 if (strcmp (cp, mp -> m_mbox) == 0)
771 case W_MBEG: /* wildcard at beginning */
772 if (strcmp (&cp[len - mp -> m_ingrp], mp -> m_mbox) == 0)
776 case W_MEND: /* wildcard at end */
777 if (strncmp (cp, mp -> m_mbox, mp -> m_ingrp) == 0)
781 case W_MBEG | W_MEND: /* wildcard at beginning & end */
782 for (i = 0; i <= len - mp -> m_ingrp; i++)
783 if (strncmp (&cp[i], mp -> m_mbox, mp -> m_ingrp) == 0)
787 for (mp = &mq; mp = mp -> m_next;) {
788 if (np -> m_mbox == NULL)
790 if ((len = strlen (cp = np -> m_mbox))
791 < (i = strlen (pp = mp -> m_mbox)))
793 switch (mp -> m_type & W_MBOX) {
799 if (!uleq (cp + len - i, pp))
806 case W_MBEG | W_MEND:
807 if (stringdex (pp, cp) < 0)
814 if (np -> m_host == NULL)
816 if ((len = strlen (cp = np -> m_host))
817 < (i = strlen (pp = mp -> m_host)))
819 switch (mp -> m_type & W_HOST) {
825 if (!uleq (cp + len - i, pp))
832 case W_HBEG | W_HEND:
833 if (stringdex (pp, cp) < 0)
839 #endif /* not BERK */