📄 parseaddr.c
字号:
/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */# include "sendmail.h"# include "errno.h"SCCSID(@(#)parseaddr.c 1.1 92/07/30 SMI); /* from UCB 5.8 3/13/88 *//*** PARSEADDR -- Parse an address**** Parses an address and breaks it up into three parts: a** net to transmit the message on, the host to transmit it** to, and a user on that host. These are loaded into an** ADDRESS header with the values squirreled away if necessary.** The "user" part may not be a real user; the process may** just reoccur on that machine. For example, on a machine** with an arpanet connection, the address** csvax.bill@berkeley** will break up to a "user" of 'csvax.bill' and a host** of 'berkeley' -- to be transmitted over the arpanet.**** Parameters:** addr -- the address to parse.** a -- a pointer to the address descriptor buffer.** If NULL, a header will be created.** copyf -- determines what shall be copied:** -1 -- don't copy anything. The printname** (q_paddr) is just addr, and the** user & host are allocated internally** to parse.** 0 -- copy out the parsed user & host, but** don't copy the printname.** +1 -- copy everything.** delim -- the character to terminate the address, passed** to prescan.**** Returns:** A pointer to the address descriptor header (`a' if** `a' is non-NULL).** NULL on error.**** Side Effects:** none*//* following delimiters are inherent to the internal algorithms */# define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ADDRESS *parseaddr(addr, a, copyf, delim) char *addr; register ADDRESS *a; int copyf; char delim;{ register char **pvp; register struct mailer *m; char pvpbuf[PSBUFSIZE]; extern char **prescan(); extern ADDRESS *buildaddr(); /* ** Initialize and prescan address. */ CurEnv->e_to = addr;# ifdef DEBUG if (tTd(20, 1)) printf("\n--parseaddr(%s)\n", addr);# endif DEBUG pvp = prescan(addr, delim, pvpbuf); if (pvp == NULL) return (NULL); /* ** Apply rewriting rules. ** Ruleset 0 does basic parsing. It must resolve. */ rewrite(pvp, 3); rewrite(pvp, 0); /* ** See if we resolved to a real mailer. */ if (pvp[0][0] != CANONNET) { setstat(EX_USAGE); usrerr("cannot resolve name"); return (NULL); } /* ** Build canonical address from pvp. */ a = buildaddr(pvp, a); if (a == NULL) return (NULL); m = a->q_mailer; /* ** Make local copies of the host & user and then ** transport them out. */ if (copyf > 0) { extern char *DelimChar; char savec = *DelimChar; *DelimChar = '\0'; a->q_paddr = newstr(addr); *DelimChar = savec; } else a->q_paddr = addr; if (a->q_user == NULL) a->q_user = ""; if (a->q_host == NULL) a->q_host = ""; if (copyf >= 0) { a->q_host = newstr(a->q_host); if (a->q_user != a->q_paddr) a->q_user = newstr(a->q_user); } /* ** Convert host name to lower case if requested. ** User name will be done later. */ if (!bitnset(M_HST_UPPER, m->m_flags)) makelower(a->q_host); /* ** Compute return value. */# ifdef DEBUG if (tTd(20, 1)) { printf("parseaddr-->"); printaddr(a, FALSE); }# endif DEBUG return (a);}/*** LOWERADDR -- map UPPER->lower case on addresses as requested.**** Parameters:** a -- address to be mapped.**** Returns:** none.**** Side Effects:** none.*/loweraddr(a) register ADDRESS *a;{ register MAILER *m = a->q_mailer; if (m != NULL && !bitnset(M_USR_UPPER, m->m_flags)) makelower(a->q_user);}/*** PRESCAN -- Prescan name and make it canonical**** Scans a name and turns it into a set of tokens. This process** deletes blanks and comments (in parentheses).**** This routine knows about quoted strings and angle brackets.**** There are certain subtleties to this routine. The one that** comes to mind now is that backslashes on the ends of names** are silently stripped off; this is intentional. The problem** is that some versions of sndmsg (like at LBL) set the kill** character to something other than @ when reading addresses;** so people type "csvax.eric\@berkeley" -- which screws up the** berknet mailer.**** Parameters:** addr -- the name to chomp.** delim -- the delimiter for the address, normally** '\0' or ','; \0 is accepted in any case.** If '\t' then we are reading the .cf file.** pvpbuf -- place to put the saved text -- note that** the pointers are static.**** Returns:** A pointer to a vector of tokens.** NULL on error.**** Side Effects:** sets DelimChar to point to the character matching 'delim'.*//* states and character types */# define OPR 0 /* operator */# define ATM 1 /* atom */# define QST 2 /* in quoted string */# define SPC 3 /* chewing up spaces */# define ONE 4 /* pick up one character */# define NSTATES 5 /* number of states */# define TYPE 017 /* mask to select state type *//* meta bits for table */# define M 020 /* meta character; don't pass through */# define B 040 /* cause a break */# define MB M|B /* meta-break */static short StateTab[NSTATES][NSTATES] ={ /* oldst chtype> OPR ATM QST SPC ONE */ /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, /*QST*/ QST, QST, OPR, QST, QST, /*SPC*/ OPR, ATM, QST, SPC|M, ONE, /*ONE*/ OPR, OPR, OPR, OPR, OPR,};# define NOCHAR -1 /* signal nothing in lookahead token */char *DelimChar; /* set to point to the delimiter */char **prescan(addr, delim, pvpbuf) char *addr; char delim; char pvpbuf[];{ register char *p; register char *q; register int c; char **avp; bool bslashmode; int cmntcnt; int anglecnt; char *tok; int state; int newstate; static char *av[MAXATOM+1]; extern int errno; /* make sure error messages don't have garbage on them */ errno = 0; q = pvpbuf; bslashmode = FALSE; cmntcnt = 0; anglecnt = 0; avp = av; state = OPR; c = NOCHAR; p = addr;# ifdef DEBUG if (tTd(22, 45)) { printf("prescan: "); xputs(p); (void) putchar('\n'); }# endif DEBUG do { /* read a token */ tok = q; for (;;) { /* store away any old lookahead character */ if (c != NOCHAR) { /* see if there is room */ if (q >= &pvpbuf[PSBUFSIZE - 5]) { usrerr("Address too long"); DelimChar = p; return (NULL); } /* squirrel it away */ *q++ = c; } /* read a new input character */ c = *p++; if (c == '\0') break; c &= ~0200;# ifdef DEBUG if (tTd(22, 101)) printf("c=%c, s=%d; ", c, state);# endif DEBUG /* chew up special characters */ *q = '\0'; /* kludge \! for naive users (violates RFC 822) */ if ((c == '!') && bslashmode) bslashmode = FALSE; if (bslashmode) { c |= 0200; bslashmode = FALSE; } else if (c == '\\') { bslashmode = TRUE; c = NOCHAR; } else if (state == QST) { /* do nothing, just avoid next clauses */ } else if (c == '(') { cmntcnt++; c = NOCHAR; } else if (c == ')') { if (cmntcnt <= 0) { usrerr("Unbalanced ')'"); DelimChar = p; return (NULL); } else cmntcnt--; } else if (cmntcnt > 0) c = NOCHAR; else if (c == '<') anglecnt++; else if (c == '>') { if (anglecnt <= 0) { usrerr("Unbalanced '>'"); DelimChar = p; return (NULL); } anglecnt--; } else if (delim == ' ' && isspace(c)) c = ' '; if (c == NOCHAR) continue; /* see if this is end of input */ if (c == delim && anglecnt <= 0 && state != QST) break; newstate = StateTab[state][toktype(c)];# ifdef DEBUG if (tTd(22, 101)) printf("ns=%02o\n", newstate);# endif DEBUG state = newstate & TYPE; if (bitset(M, newstate)) c = NOCHAR; if (bitset(B, newstate)) break; } /* new token */ if (tok != q) { *q++ = '\0';# ifdef DEBUG if (tTd(22, 36)) { printf("tok="); xputs(tok); (void) putchar('\n'); }# endif DEBUG if (avp >= &av[MAXATOM]) { syserr("prescan: too many tokens"); DelimChar = p; return (NULL); } *avp++ = tok; } } while (c != '\0' && (c != delim || anglecnt > 0)); *avp = NULL; DelimChar = --p; if (cmntcnt > 0) usrerr("Unbalanced '('"); else if (anglecnt > 0) usrerr("Unbalanced '<'"); else if (state == QST) usrerr("Unbalanced '\"'"); else if (av[0] != NULL) return (av); return (NULL);}/*** TOKTYPE -- return token type**** Parameters:** c -- the character in question.**** Returns:** Its type.**** Side Effects:** none.*/toktype(c) register char c;{ static char buf[50]; static bool firstime = TRUE; if (firstime) { firstime = FALSE; expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); (void) strcat(buf, DELIMCHARS); } if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS || c == MATCHYELLOW || c == MATCHNYELLOW) return (ONE); if (c == '"') return (QST); if (!isascii(c)) return (ATM); if (isspace(c) || c == ')') return (SPC); if (iscntrl(c) || index(buf, c) != NULL) return (OPR); return (ATM);}/*** REWRITE -- apply rewrite rules to token vector.**** This routine is an ordered production system. Each rewrite** rule has a LHS (called the pattern) and a RHS (called the** rewrite); 'rwr' points the the current rewrite rule.**** For each rewrite rule, 'avp' points the address vector we** are trying to match against, and 'pvp' points to the pattern.** If pvp points to a special match value (MATCHZANY, MATCHANY,** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp** matched is saved away in the match vector (pointed to by 'mvp').**** When a match between avp & pvp does not match, we try to** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS** we must also back out the match in mvp. If we reach a** MATCHANY or MATCHZANY we just extend the match and start** over again.**** When we finally match, we rewrite the address vector** and try over again.**** Parameters:** pvp -- pointer to token vector.**** Returns:** none.**** Side Effects:** pvp is modified.*/struct match{ char **first; /* first token matched */ char **last; /* last token matched */};# define MAXMATCH 9 /* max params per rewrite */rewrite(pvp, ruleset) char **pvp; int ruleset;{ register char *ap; /* address pointer */ register char *rp; /* rewrite pointer */ register char **avp; /* address vector pointer */ register char **rvp; /* rewrite vector pointer */ register struct match *mlp; /* cur ptr into mlist */ register struct rewrite *rwr; /* pointer to current rewrite rule */ struct match mlist[MAXMATCH]; /* stores match on LHS */ char *npvp[MAXATOM+1]; /* temporary space for rebuild */ char tokbuf[MAXNAME+1]; /* for concatenated class tokens */ if (OpMode == MD_TEST || tTd(21, 2)) { printf("rewrite: ruleset %2d input:", ruleset); printav(pvp); } if (pvp == NULL) return; /* ** Run through the list of rewrite rules, applying ** any that match. */ for (rwr = RewriteRules[ruleset]; rwr != NULL; ) {# ifdef DEBUG if (tTd(21, 12)) { printf("-----trying rule:"); printav(rwr->r_lhs); }# endif DEBUG /* try to match on this rule */ mlp = mlist; rvp = rwr->r_lhs; avp = pvp; while ((ap = *avp) != NULL || *rvp != NULL) { rp = *rvp;# ifdef DEBUG if (tTd(21, 35)) { printf("ap="); xputs(ap); printf(", rp="); xputs(rp); printf("\n"); }# endif DEBUG if (rp == NULL) { /* end-of-pattern before end-of-address */ goto backup; } if (ap == NULL && *rp != MATCHZANY) { /* end-of-input */ break; } switch (*rp) { register STAB *s; char **oldavp; case MATCHNCLASS: /* match any single token not in a class */ s = stab(ap, ST_CLASS, ST_FIND); if (s != NULL && bitnset(rp[1], s->s_class)) goto backup; /* match exactly one token */ mlp->first = avp; mlp->last = avp++; mlp++; break; case MATCHCLASS: /* match any token in a class */ /* from lel@ida.liu.se */ oldavp = avp; *tokbuf = NULL; do { if (*avp == NULL) { avp = oldavp; goto backup; } (void) strcat(tokbuf, *avp++); s = stab(tokbuf, ST_CLASS, ST_FIND); } while (s == NULL || !bitnset(rp[1], s->s_class)); mlp->first = oldavp; mlp->last = avp-1; mlp++; break; case MATCHONE: case MATCHANY: /* match exactly one token */ mlp->first = avp; mlp->last = avp++; mlp++; break; case MATCHZANY: /* match zero or more tokens */ mlp->first = avp; mlp->last = avp - 1; mlp++; break;# ifdef YELLOW case MATCHNYELLOW: case MATCHYELLOW: /* match any token in (not in) a NIS map. */ if (yellowmatch(ap,rp[1])) { if (*rp == MATCHNYELLOW) goto backup; } else if (*rp == MATCHYELLOW) goto backup; mlp->first = avp; mlp->last = avp++; mlp++; break;# endif YELLOW default: /* must have exact match */ if (strcasecmp(rp, ap)) goto backup; avp++; break; } /* successful match on this token */ rvp++; continue; backup: /* match failed -- back up */ while (--rvp >= rwr->r_lhs) { rp = *rvp; if (*rp == MATCHCLASS) { register STAB *s; char **oldavp; /* attempt to extend binding */ /* slow, concat version by lel@ida.liu.se */ oldavp = avp; *tokbuf = NULL; for (avp = mlp[-1].first; avp <= mlp[-1].last; avp++) (void)strcat(tokbuf, *avp); do { if (*avp == NULL) { /* back out binding */ avp = oldavp; mlp--; goto cantextend;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -