138032Speter/* 2261194Sgshapiro * Copyright (c) 1998-2006 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1538032Speter 16266527SgshapiroSM_RCSID("@(#)$Id: parseaddr.c,v 8.407 2013-11-22 20:51:56 ca Exp $") 1790792Sgshapiro 18168515Sgshapiro#include <sm/sendmail.h> 19168515Sgshapiro#include "map.h" 20168515Sgshapiro 2190792Sgshapirostatic void allocaddr __P((ADDRESS *, int, char *, ENVELOPE *)); 2264562Sgshapirostatic int callsubr __P((char**, int, ENVELOPE *)); 2364562Sgshapirostatic char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *)); 2464562Sgshapirostatic ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); 2594334Sgshapirostatic bool hasctrlchar __P((register char *, bool, bool)); 2664562Sgshapiro 2794334Sgshapiro/* replacement for illegal characters in addresses */ 2894334Sgshapiro#define BAD_CHAR_REPLACEMENT '?' 2994334Sgshapiro 3038032Speter/* 3138032Speter** PARSEADDR -- Parse an address 3238032Speter** 3338032Speter** Parses an address and breaks it up into three parts: a 3438032Speter** net to transmit the message on, the host to transmit it 3538032Speter** to, and a user on that host. These are loaded into an 3638032Speter** ADDRESS header with the values squirreled away if necessary. 3738032Speter** The "user" part may not be a real user; the process may 3838032Speter** just reoccur on that machine. For example, on a machine 3938032Speter** with an arpanet connection, the address 4038032Speter** csvax.bill@berkeley 4138032Speter** will break up to a "user" of 'csvax.bill' and a host 4238032Speter** of 'berkeley' -- to be transmitted over the arpanet. 4338032Speter** 4438032Speter** Parameters: 4538032Speter** addr -- the address to parse. 4638032Speter** a -- a pointer to the address descriptor buffer. 4790792Sgshapiro** If NULL, an address will be created. 4838032Speter** flags -- describe detail for parsing. See RF_ definitions 4938032Speter** in sendmail.h. 5038032Speter** delim -- the character to terminate the address, passed 5138032Speter** to prescan. 5238032Speter** delimptr -- if non-NULL, set to the location of the 5338032Speter** delim character that was found. 5438032Speter** e -- the envelope that will contain this address. 5590792Sgshapiro** isrcpt -- true if the address denotes a recipient; false 5690792Sgshapiro** indicates a sender. 5738032Speter** 5838032Speter** Returns: 5938032Speter** A pointer to the address descriptor header (`a' if 6038032Speter** `a' is non-NULL). 6138032Speter** NULL on error. 6238032Speter** 6338032Speter** Side Effects: 6490792Sgshapiro** e->e_to = addr 6538032Speter*/ 6638032Speter 6738032Speter/* following delimiters are inherent to the internal algorithms */ 6864562Sgshapiro#define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ 6938032Speter 7038032SpeterADDRESS * 7190792Sgshapiroparseaddr(addr, a, flags, delim, delimptr, e, isrcpt) 7238032Speter char *addr; 7338032Speter register ADDRESS *a; 7438032Speter int flags; 7538032Speter int delim; 7638032Speter char **delimptr; 7738032Speter register ENVELOPE *e; 7890792Sgshapiro bool isrcpt; 7938032Speter{ 8090792Sgshapiro char **pvp; 8138032Speter auto char *delimptrbuf; 8264562Sgshapiro bool qup; 8338032Speter char pvpbuf[PSBUFSIZE]; 8438032Speter 8538032Speter /* 8638032Speter ** Initialize and prescan address. 8738032Speter */ 8838032Speter 8938032Speter e->e_to = addr; 9038032Speter if (tTd(20, 1)) 9190792Sgshapiro sm_dprintf("\n--parseaddr(%s)\n", addr); 9238032Speter 9338032Speter if (delimptr == NULL) 9438032Speter delimptr = &delimptrbuf; 9538032Speter 96168515Sgshapiro pvp = prescan(addr, delim, pvpbuf, sizeof(pvpbuf), delimptr, 97168515Sgshapiro ExtTokenTab, false); 9838032Speter if (pvp == NULL) 9938032Speter { 10038032Speter if (tTd(20, 1)) 10190792Sgshapiro sm_dprintf("parseaddr-->NULL\n"); 10264562Sgshapiro return NULL; 10338032Speter } 10438032Speter 10590792Sgshapiro if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt)) 10638032Speter { 10738032Speter if (tTd(20, 1)) 10890792Sgshapiro sm_dprintf("parseaddr-->bad address\n"); 10938032Speter return NULL; 11038032Speter } 11138032Speter 11238032Speter /* 11338032Speter ** Save addr if we are going to have to. 11438032Speter ** 11538032Speter ** We have to do this early because there is a chance that 11638032Speter ** the map lookups in the rewriting rules could clobber 11738032Speter ** static memory somewhere. 11838032Speter */ 11938032Speter 12038032Speter if (bitset(RF_COPYPADDR, flags) && addr != NULL) 12138032Speter { 12238032Speter char savec = **delimptr; 12338032Speter 12438032Speter if (savec != '\0') 12538032Speter **delimptr = '\0'; 12690792Sgshapiro e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr); 12738032Speter if (savec != '\0') 12838032Speter **delimptr = savec; 12938032Speter } 13038032Speter 13138032Speter /* 13238032Speter ** Apply rewriting rules. 13338032Speter ** Ruleset 0 does basic parsing. It must resolve. 13438032Speter */ 13538032Speter 13690792Sgshapiro qup = false; 137363466Sgshapiro e->e_flags |= EF_SECURE; 13890792Sgshapiro if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 13990792Sgshapiro qup = true; 14090792Sgshapiro if (REWRITE(pvp, 0, e) == EX_TEMPFAIL) 14190792Sgshapiro qup = true; 14238032Speter 14338032Speter /* 14438032Speter ** Build canonical address from pvp. 14538032Speter */ 14638032Speter 14738032Speter a = buildaddr(pvp, a, flags, e); 14838032Speter 14994334Sgshapiro if (hasctrlchar(a->q_user, isrcpt, true)) 15090792Sgshapiro { 15190792Sgshapiro if (tTd(20, 1)) 15290792Sgshapiro sm_dprintf("parseaddr-->bad q_user\n"); 15394334Sgshapiro 15494334Sgshapiro /* 15594334Sgshapiro ** Just mark the address as bad so DSNs work. 15694334Sgshapiro ** hasctrlchar() has to make sure that the address 15794334Sgshapiro ** has been sanitized, e.g., shortened. 15894334Sgshapiro */ 15994334Sgshapiro 16094334Sgshapiro a->q_state = QS_BADADDR; 16190792Sgshapiro } 16290792Sgshapiro 16338032Speter /* 16438032Speter ** Make local copies of the host & user and then 16538032Speter ** transport them out. 16638032Speter */ 16738032Speter 16890792Sgshapiro allocaddr(a, flags, addr, e); 169363466Sgshapiro e->e_flags &= ~EF_SECURE; 17064562Sgshapiro if (QS_IS_BADADDR(a->q_state)) 17194334Sgshapiro { 17294334Sgshapiro /* weed out bad characters in the printable address too */ 17394334Sgshapiro (void) hasctrlchar(a->q_paddr, isrcpt, false); 17438032Speter return a; 17594334Sgshapiro } 17638032Speter 17738032Speter /* 17890792Sgshapiro ** Select a queue directory for recipient addresses. 17990792Sgshapiro ** This is done here and in split_across_queue_groups(), 18090792Sgshapiro ** but the latter applies to addresses after aliasing, 18190792Sgshapiro ** and only if splitting is done. 18290792Sgshapiro */ 18390792Sgshapiro 18490792Sgshapiro if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) && 185168515Sgshapiro !bitset(RF_SENDERADDR|RF_HEADERADDR|RF_RM_ADDR, flags) && 18690792Sgshapiro OpMode != MD_INITALIAS) 18790792Sgshapiro { 18890792Sgshapiro int r; 18990792Sgshapiro 19090792Sgshapiro /* call ruleset which should return a queue group name */ 19190792Sgshapiro r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf, 19290792Sgshapiro sizeof(pvpbuf)); 19390792Sgshapiro if (r == EX_OK && 19490792Sgshapiro pvp != NULL && pvp[0] != NULL && 19590792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 19690792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 19790792Sgshapiro { 19890792Sgshapiro r = name2qid(pvp[1]); 19990792Sgshapiro if (r == NOQGRP && LogLevel > 10) 20090792Sgshapiro sm_syslog(LOG_INFO, NOQID, 20190792Sgshapiro "can't find queue group name %s, selection ignored", 20290792Sgshapiro pvp[1]); 20390792Sgshapiro if (tTd(20, 4) && r != NOQGRP) 20490792Sgshapiro sm_syslog(LOG_INFO, NOQID, 20590792Sgshapiro "queue group name %s -> %d", 20690792Sgshapiro pvp[1], r); 20790792Sgshapiro a->q_qgrp = r == NOQGRP ? ENVQGRP : r; 20890792Sgshapiro } 20990792Sgshapiro } 21090792Sgshapiro 21190792Sgshapiro /* 21238032Speter ** If there was a parsing failure, mark it for queueing. 21338032Speter */ 21438032Speter 21564562Sgshapiro if (qup && OpMode != MD_INITALIAS) 21638032Speter { 21738032Speter char *msg = "Transient parse error -- message queued for future delivery"; 21838032Speter 21938032Speter if (e->e_sendmode == SM_DEFER) 22038032Speter msg = "Deferring message until queue run"; 22138032Speter if (tTd(20, 1)) 222173340Sgshapiro sm_dprintf("parseaddr: queueing message\n"); 223363466Sgshapiro message("%s", msg); 22438032Speter if (e->e_message == NULL && e->e_sendmode != SM_DEFER) 22590792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, msg); 22664562Sgshapiro a->q_state = QS_QUEUEUP; 22738032Speter a->q_status = "4.4.3"; 22838032Speter } 22938032Speter 23038032Speter /* 23138032Speter ** Compute return value. 23238032Speter */ 23338032Speter 23438032Speter if (tTd(20, 1)) 23538032Speter { 23690792Sgshapiro sm_dprintf("parseaddr-->"); 237132943Sgshapiro printaddr(sm_debug_file(), a, false); 23838032Speter } 23938032Speter 24064562Sgshapiro return a; 24138032Speter} 24290792Sgshapiro/* 24390792Sgshapiro** INVALIDADDR -- check for address containing characters used for macros 24438032Speter** 24538032Speter** Parameters: 24638032Speter** addr -- the address to check. 247244833Sgshapiro** note: this is the complete address (including display part) 24890792Sgshapiro** delimptr -- if non-NULL: end of address to check, i.e., 24990792Sgshapiro** a pointer in the address string. 25090792Sgshapiro** isrcpt -- true iff the address is for a recipient. 25138032Speter** 25238032Speter** Returns: 25390792Sgshapiro** true -- if the address has characters that are reservered 25490792Sgshapiro** for macros or is too long. 25590792Sgshapiro** false -- otherwise. 25638032Speter*/ 25738032Speter 25838032Speterbool 25990792Sgshapiroinvalidaddr(addr, delimptr, isrcpt) 26038032Speter register char *addr; 26138032Speter char *delimptr; 26290792Sgshapiro bool isrcpt; 26338032Speter{ 26490792Sgshapiro bool result = false; 26538032Speter char savedelim = '\0'; 26694334Sgshapiro char *b = addr; 26790792Sgshapiro int len = 0; 26838032Speter 26938032Speter if (delimptr != NULL) 27038032Speter { 27190792Sgshapiro /* delimptr points to the end of the address to test */ 27238032Speter savedelim = *delimptr; 27390792Sgshapiro if (savedelim != '\0') /* if that isn't '\0' already: */ 27490792Sgshapiro *delimptr = '\0'; /* set it */ 27538032Speter } 27690792Sgshapiro for (; *addr != '\0'; addr++) 27738032Speter { 278363466Sgshapiro#if !_FFR_EAI 279168515Sgshapiro if (!EightBitAddrOK && (*addr & 0340) == 0200) 28090792Sgshapiro { 28190792Sgshapiro setstat(EX_USAGE); 28290792Sgshapiro result = true; 28394334Sgshapiro *addr = BAD_CHAR_REPLACEMENT; 28490792Sgshapiro } 285363466Sgshapiro#endif 28690792Sgshapiro if (++len > MAXNAME - 1) 28790792Sgshapiro { 28894334Sgshapiro char saved = *addr; 28994334Sgshapiro 29094334Sgshapiro *addr = '\0'; 29194334Sgshapiro usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)", 29294334Sgshapiro b, MAXNAME - 1); 29394334Sgshapiro *addr = saved; 29490792Sgshapiro result = true; 29590792Sgshapiro goto delim; 29690792Sgshapiro } 29738032Speter } 29890792Sgshapiro if (result) 29990792Sgshapiro { 30090792Sgshapiro if (isrcpt) 30194334Sgshapiro usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"", 30294334Sgshapiro b); 30390792Sgshapiro else 30494334Sgshapiro usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"", 30594334Sgshapiro b); 30690792Sgshapiro } 30790792Sgshapirodelim: 30890792Sgshapiro if (delimptr != NULL && savedelim != '\0') 30990792Sgshapiro *delimptr = savedelim; /* restore old character at delimptr */ 31090792Sgshapiro return result; 31190792Sgshapiro} 31290792Sgshapiro/* 31390792Sgshapiro** HASCTRLCHAR -- check for address containing meta-characters 31490792Sgshapiro** 31590792Sgshapiro** Checks that the address contains no meta-characters, and contains 31690792Sgshapiro** no "non-printable" characters unless they are quoted or escaped. 31790792Sgshapiro** Quoted or escaped characters are literals. 31890792Sgshapiro** 31990792Sgshapiro** Parameters: 32090792Sgshapiro** addr -- the address to check. 32190792Sgshapiro** isrcpt -- true if the address is for a recipient; false 32290792Sgshapiro** indicates a from. 32394334Sgshapiro** complain -- true if an error should issued if the address 32494334Sgshapiro** is invalid and should be "repaired". 32590792Sgshapiro** 32690792Sgshapiro** Returns: 327223067Sgshapiro** true -- if the address has any "weird" characters or 32890792Sgshapiro** non-printable characters or if a quote is unbalanced. 32990792Sgshapiro** false -- otherwise. 33090792Sgshapiro*/ 33190792Sgshapiro 33290792Sgshapirostatic bool 33394334Sgshapirohasctrlchar(addr, isrcpt, complain) 33490792Sgshapiro register char *addr; 33594334Sgshapiro bool isrcpt, complain; 33690792Sgshapiro{ 33794334Sgshapiro bool quoted = false; 33890792Sgshapiro int len = 0; 33994334Sgshapiro char *result = NULL; 34094334Sgshapiro char *b = addr; 34190792Sgshapiro 34290792Sgshapiro if (addr == NULL) 34390792Sgshapiro return false; 34438032Speter for (; *addr != '\0'; addr++) 34538032Speter { 34694334Sgshapiro if (++len > MAXNAME - 1) 34794334Sgshapiro { 34894334Sgshapiro if (complain) 34994334Sgshapiro { 35094334Sgshapiro (void) shorten_rfc822_string(b, MAXNAME - 1); 35194334Sgshapiro usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)", 35294334Sgshapiro b, MAXNAME - 1); 35394334Sgshapiro return true; 35494334Sgshapiro } 35594334Sgshapiro result = "too long"; 35694334Sgshapiro } 357363466Sgshapiro if (!quoted && ((unsigned char)*addr < 32 || *addr == 127)) 35890792Sgshapiro { 35994334Sgshapiro result = "non-printable character"; 36094334Sgshapiro *addr = BAD_CHAR_REPLACEMENT; 36194334Sgshapiro continue; 36290792Sgshapiro } 36390792Sgshapiro if (*addr == '"') 36490792Sgshapiro quoted = !quoted; 36590792Sgshapiro else if (*addr == '\\') 36690792Sgshapiro { 36790792Sgshapiro /* XXX Generic problem: no '\0' in strings. */ 36890792Sgshapiro if (*++addr == '\0') 36990792Sgshapiro { 37094334Sgshapiro result = "trailing \\ character"; 37194334Sgshapiro *--addr = BAD_CHAR_REPLACEMENT; 37290792Sgshapiro break; 37390792Sgshapiro } 37490792Sgshapiro } 375363466Sgshapiro#if !_FFR_EAI 376168515Sgshapiro if (!EightBitAddrOK && (*addr & 0340) == 0200) 37790792Sgshapiro { 37890792Sgshapiro setstat(EX_USAGE); 37994334Sgshapiro result = "8-bit character"; 38094334Sgshapiro *addr = BAD_CHAR_REPLACEMENT; 38194334Sgshapiro continue; 38290792Sgshapiro } 383363466Sgshapiro#endif 38438032Speter } 38590792Sgshapiro if (quoted) 38694334Sgshapiro result = "unbalanced quote"; /* unbalanced quote */ 38794334Sgshapiro if (result != NULL && complain) 38838032Speter { 38990792Sgshapiro if (isrcpt) 39094334Sgshapiro usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)", 39194334Sgshapiro b, result); 39290792Sgshapiro else 39394334Sgshapiro usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)", 39494334Sgshapiro b, result); 39538032Speter } 39694334Sgshapiro return result != NULL; 39738032Speter} 39890792Sgshapiro/* 39938032Speter** ALLOCADDR -- do local allocations of address on demand. 40038032Speter** 40138032Speter** Also lowercases the host name if requested. 40238032Speter** 40338032Speter** Parameters: 40438032Speter** a -- the address to reallocate. 40538032Speter** flags -- the copy flag (see RF_ definitions in sendmail.h 40638032Speter** for a description). 40738032Speter** paddr -- the printname of the address. 40890792Sgshapiro** e -- envelope 40938032Speter** 41038032Speter** Returns: 41138032Speter** none. 41238032Speter** 41338032Speter** Side Effects: 41438032Speter** Copies portions of a into local buffers as requested. 41538032Speter*/ 41638032Speter 41764562Sgshapirostatic void 41890792Sgshapiroallocaddr(a, flags, paddr, e) 41938032Speter register ADDRESS *a; 42038032Speter int flags; 42138032Speter char *paddr; 42290792Sgshapiro ENVELOPE *e; 42338032Speter{ 42438032Speter if (tTd(24, 4)) 425363466Sgshapiro sm_dprintf("allocaddr(flags=%x, paddr=%s, ad=%d)\n", flags, paddr, bitset(EF_SECURE, e->e_flags)); 42638032Speter 42738032Speter a->q_paddr = paddr; 42838032Speter 42938032Speter if (a->q_user == NULL) 43090792Sgshapiro a->q_user = ""; 43138032Speter if (a->q_host == NULL) 43290792Sgshapiro a->q_host = ""; 43338032Speter 434363466Sgshapiro if (bitset(EF_SECURE, e->e_flags)) 435363466Sgshapiro a->q_flags |= QSECURE; 436363466Sgshapiro 43738032Speter if (bitset(RF_COPYPARSE, flags)) 43838032Speter { 43990792Sgshapiro a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host); 44038032Speter if (a->q_user != a->q_paddr) 44190792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user); 44238032Speter } 44338032Speter 44438032Speter if (a->q_paddr == NULL) 44590792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 44690792Sgshapiro a->q_qgrp = NOAQGRP; 44738032Speter} 448168515Sgshapiro 44990792Sgshapiro/* 45038032Speter** PRESCAN -- Prescan name and make it canonical 45138032Speter** 45238032Speter** Scans a name and turns it into a set of tokens. This process 45364562Sgshapiro** deletes blanks and comments (in parentheses) (if the token type 45464562Sgshapiro** for left paren is SPC). 45538032Speter** 45638032Speter** This routine knows about quoted strings and angle brackets. 45738032Speter** 45838032Speter** There are certain subtleties to this routine. The one that 45938032Speter** comes to mind now is that backslashes on the ends of names 46038032Speter** are silently stripped off; this is intentional. The problem 46138032Speter** is that some versions of sndmsg (like at LBL) set the kill 46238032Speter** character to something other than @ when reading addresses; 46338032Speter** so people type "csvax.eric\@berkeley" -- which screws up the 46438032Speter** berknet mailer. 46538032Speter** 46638032Speter** Parameters: 46738032Speter** addr -- the name to chomp. 46838032Speter** delim -- the delimiter for the address, normally 46938032Speter** '\0' or ','; \0 is accepted in any case. 47038032Speter** If '\t' then we are reading the .cf file. 47138032Speter** pvpbuf -- place to put the saved text -- note that 47238032Speter** the pointers are static. 47338032Speter** pvpbsize -- size of pvpbuf. 47438032Speter** delimptr -- if non-NULL, set to the location of the 47538032Speter** terminating delimiter. 47638032Speter** toktab -- if set, a token table to use for parsing. 47738032Speter** If NULL, use the default table. 478132943Sgshapiro** ignore -- if true, ignore unbalanced addresses 47938032Speter** 48038032Speter** Returns: 48138032Speter** A pointer to a vector of tokens. 48238032Speter** NULL on error. 48338032Speter*/ 48438032Speter 48538032Speter/* states and character types */ 48664562Sgshapiro#define OPR 0 /* operator */ 48764562Sgshapiro#define ATM 1 /* atom */ 48864562Sgshapiro#define QST 2 /* in quoted string */ 48964562Sgshapiro#define SPC 3 /* chewing up spaces */ 49064562Sgshapiro#define ONE 4 /* pick up one character */ 49164562Sgshapiro#define ILL 5 /* illegal character */ 49238032Speter 49364562Sgshapiro#define NSTATES 6 /* number of states */ 49464562Sgshapiro#define TYPE 017 /* mask to select state type */ 49538032Speter 49638032Speter/* meta bits for table */ 49764562Sgshapiro#define M 020 /* meta character; don't pass through */ 49864562Sgshapiro#define B 040 /* cause a break */ 49964562Sgshapiro#define MB M|B /* meta-break */ 50038032Speter 50138032Speterstatic short StateTab[NSTATES][NSTATES] = 50238032Speter{ 50338032Speter /* oldst chtype> OPR ATM QST SPC ONE ILL */ 50438032Speter /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, 50538032Speter /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, 50638032Speter /*QST*/ { QST, QST, OPR, QST, QST, QST }, 50738032Speter /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, 50838032Speter /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, 509168515Sgshapiro /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M } 51038032Speter}; 51138032Speter 512168515Sgshapiro/* these all get modified with the OperatorChars */ 513168515Sgshapiro 514168515Sgshapiro/* token type table for external strings */ 515168515Sgshapirounsigned char ExtTokenTab[256] = 51638032Speter{ 51738032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 51838032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 51938032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 52038032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52138032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 52264562Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 52338032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 52438032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52538032Speter /* @ A B C D E F G H I J K L M N O */ 52638032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52738032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 52838032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52938032Speter /* ` a b c d e f g h i j k l m n o */ 53038032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 53138032Speter /* p q r s t u v w x y z { | } ~ del */ 53238032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 53338032Speter 53438032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 535168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 536168515Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 537168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 538168515Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 539168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 540168515Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 541168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 542168515Sgshapiro /* @ A B C D E F G H I J K L M N O */ 543168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 544168515Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 545168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 546168515Sgshapiro /* ` a b c d e f g h i j k l m n o */ 547168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 548168515Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 549168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM 550168515Sgshapiro}; 551168515Sgshapiro 552168515Sgshapiro/* token type table for internal strings */ 553168515Sgshapirounsigned char IntTokenTab[256] = 554168515Sgshapiro{ 555168515Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 556168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 557168515Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 558168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 559168515Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 560168515Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 561168515Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 562168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 563168515Sgshapiro /* @ A B C D E F G H I J K L M N O */ 564168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 565168515Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 566168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 567168515Sgshapiro /* ` a b c d e f g h i j k l m n o */ 568168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 569168515Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 570168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 571168515Sgshapiro 572168515Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 57338032Speter OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 57438032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 57538032Speter OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 57638032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 57738032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 57838032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 57938032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 58038032Speter /* @ A B C D E F G H I J K L M N O */ 58138032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 58238032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 58338032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 58438032Speter /* ` a b c d e f g h i j k l m n o */ 58538032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 58638032Speter /* p q r s t u v w x y z { | } ~ del */ 587168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE 58838032Speter}; 58938032Speter 59038032Speter/* token type table for MIME parsing */ 59190792Sgshapirounsigned char MimeTokenTab[256] = 59238032Speter{ 59338032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 59438032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL, 59538032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 59638032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 59738032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 59864562Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR, 59938032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 60038032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR, 60138032Speter /* @ A B C D E F G H I J K L M N O */ 60238032Speter OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 60338032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 60438032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM, 60538032Speter /* ` a b c d e f g h i j k l m n o */ 60638032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 60738032Speter /* p q r s t u v w x y z { | } ~ del */ 60838032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 60938032Speter 61038032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 61138032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61238032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 61338032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61438032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 61538032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61638032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 61738032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61838032Speter /* @ A B C D E F G H I J K L M N O */ 61938032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 62038032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 62138032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 62238032Speter /* ` a b c d e f g h i j k l m n o */ 62338032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 62438032Speter /* p q r s t u v w x y z { | } ~ del */ 625168515Sgshapiro ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ONE 62638032Speter}; 62738032Speter 62864562Sgshapiro/* token type table: don't strip comments */ 62990792Sgshapirounsigned char TokTypeNoC[256] = 63064562Sgshapiro{ 63164562Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 63264562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 63364562Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 63464562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63564562Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 63664562Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM, 63764562Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 63864562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63964562Sgshapiro /* @ A B C D E F G H I J K L M N O */ 64064562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64164562Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 64264562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64364562Sgshapiro /* ` a b c d e f g h i j k l m n o */ 64464562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64564562Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 64664562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64738032Speter 64864562Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 64964562Sgshapiro OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 65064562Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 65164562Sgshapiro OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 65264562Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 65364562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 65464562Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 65564562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 65664562Sgshapiro /* @ A B C D E F G H I J K L M N O */ 65764562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 65864562Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 65964562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 66064562Sgshapiro /* ` a b c d e f g h i j k l m n o */ 66164562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 66264562Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 663168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE 66464562Sgshapiro}; 66538032Speter 66664562Sgshapiro 667112810Sgshapiro#define NOCHAR (-1) /* signal nothing in lookahead token */ 66864562Sgshapiro 66938032Speterchar ** 670132943Sgshapiroprescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore) 67138032Speter char *addr; 67238032Speter int delim; 67338032Speter char pvpbuf[]; 67438032Speter int pvpbsize; 67538032Speter char **delimptr; 67690792Sgshapiro unsigned char *toktab; 677132943Sgshapiro bool ignore; 67838032Speter{ 67938032Speter register char *p; 68038032Speter register char *q; 68138032Speter register int c; 68238032Speter char **avp; 68338032Speter bool bslashmode; 68438032Speter bool route_syntax; 68538032Speter int cmntcnt; 68638032Speter int anglecnt; 68738032Speter char *tok; 68838032Speter int state; 68938032Speter int newstate; 69038032Speter char *saveto = CurEnv->e_to; 69164562Sgshapiro static char *av[MAXATOM + 1]; 69290792Sgshapiro static bool firsttime = true; 69338032Speter 69438032Speter if (firsttime) 69538032Speter { 69638032Speter /* initialize the token type table */ 69738032Speter char obuf[50]; 69838032Speter 69990792Sgshapiro firsttime = false; 70038032Speter if (OperatorChars == NULL) 70138032Speter { 70238032Speter if (ConfigLevel < 7) 70338032Speter OperatorChars = macvalue('o', CurEnv); 70438032Speter if (OperatorChars == NULL) 70538032Speter OperatorChars = ".:@[]"; 70638032Speter } 707168515Sgshapiro expand(OperatorChars, obuf, sizeof(obuf) - sizeof(DELIMCHARS), 70864562Sgshapiro CurEnv); 709168515Sgshapiro (void) sm_strlcat(obuf, DELIMCHARS, sizeof(obuf)); 71038032Speter for (p = obuf; *p != '\0'; p++) 71138032Speter { 712168515Sgshapiro if (IntTokenTab[*p & 0xff] == ATM) 713168515Sgshapiro IntTokenTab[*p & 0xff] = OPR; 714168515Sgshapiro if (ExtTokenTab[*p & 0xff] == ATM) 715168515Sgshapiro ExtTokenTab[*p & 0xff] = OPR; 71664562Sgshapiro if (TokTypeNoC[*p & 0xff] == ATM) 71764562Sgshapiro TokTypeNoC[*p & 0xff] = OPR; 71838032Speter } 71938032Speter } 72038032Speter if (toktab == NULL) 721168515Sgshapiro toktab = ExtTokenTab; 72238032Speter 72338032Speter /* make sure error messages don't have garbage on them */ 72438032Speter errno = 0; 72538032Speter 72638032Speter q = pvpbuf; 72790792Sgshapiro bslashmode = false; 72890792Sgshapiro route_syntax = false; 72938032Speter cmntcnt = 0; 73038032Speter anglecnt = 0; 73138032Speter avp = av; 73238032Speter state = ATM; 73338032Speter c = NOCHAR; 73438032Speter p = addr; 73538032Speter CurEnv->e_to = p; 73638032Speter if (tTd(22, 11)) 73738032Speter { 73890792Sgshapiro sm_dprintf("prescan: "); 739132943Sgshapiro xputs(sm_debug_file(), p); 74090792Sgshapiro sm_dprintf("\n"); 74138032Speter } 74238032Speter 74338032Speter do 74438032Speter { 74538032Speter /* read a token */ 74638032Speter tok = q; 74738032Speter for (;;) 74838032Speter { 74938032Speter /* store away any old lookahead character */ 75038032Speter if (c != NOCHAR && !bslashmode) 75138032Speter { 75238032Speter /* see if there is room */ 75338032Speter if (q >= &pvpbuf[pvpbsize - 5]) 75438032Speter { 755112810Sgshapiro addrtoolong: 75664562Sgshapiro usrerr("553 5.1.1 Address too long"); 75790792Sgshapiro if (strlen(addr) > MAXNAME) 75838032Speter addr[MAXNAME] = '\0'; 75938032Speter returnnull: 76038032Speter if (delimptr != NULL) 761120169Snectar { 762120169Snectar if (p > addr) 763120256Sgshapiro --p; 76438032Speter *delimptr = p; 765120169Snectar } 76638032Speter CurEnv->e_to = saveto; 76764562Sgshapiro return NULL; 76838032Speter } 76938032Speter 77038032Speter /* squirrel it away */ 771112810Sgshapiro#if !ALLOW_255 772168515Sgshapiro if ((char) c == (char) -1 && !tTd(82, 101) && 773168515Sgshapiro !EightBitAddrOK) 774112810Sgshapiro c &= 0x7f; 775112810Sgshapiro#endif /* !ALLOW_255 */ 77638032Speter *q++ = c; 77738032Speter } 77838032Speter 77938032Speter /* read a new input character */ 780112810Sgshapiro c = (*p++) & 0x00ff; 78138032Speter if (c == '\0') 78238032Speter { 78338032Speter /* diagnose and patch up bad syntax */ 784132943Sgshapiro if (ignore) 785132943Sgshapiro break; 786132943Sgshapiro else if (state == QST) 78738032Speter { 78890792Sgshapiro usrerr("553 Unbalanced '\"'"); 78938032Speter c = '"'; 79038032Speter } 79138032Speter else if (cmntcnt > 0) 79238032Speter { 79390792Sgshapiro usrerr("553 Unbalanced '('"); 79438032Speter c = ')'; 79538032Speter } 79638032Speter else if (anglecnt > 0) 79738032Speter { 79838032Speter c = '>'; 79990792Sgshapiro usrerr("553 Unbalanced '<'"); 80038032Speter } 80138032Speter else 80238032Speter break; 80338032Speter 80438032Speter p--; 80538032Speter } 80638032Speter else if (c == delim && cmntcnt <= 0 && state != QST) 80738032Speter { 80838032Speter if (anglecnt <= 0) 80938032Speter break; 81038032Speter 81138032Speter /* special case for better error management */ 812132943Sgshapiro if (delim == ',' && !route_syntax && !ignore) 81338032Speter { 81490792Sgshapiro usrerr("553 Unbalanced '<'"); 81538032Speter c = '>'; 81638032Speter p--; 81738032Speter } 81838032Speter } 81938032Speter 82038032Speter if (tTd(22, 101)) 82190792Sgshapiro sm_dprintf("c=%c, s=%d; ", c, state); 82238032Speter 82338032Speter /* chew up special characters */ 82438032Speter *q = '\0'; 82538032Speter if (bslashmode) 82638032Speter { 82790792Sgshapiro bslashmode = false; 82838032Speter 82938032Speter /* kludge \! for naive users */ 83038032Speter if (cmntcnt > 0) 83138032Speter { 83238032Speter c = NOCHAR; 83338032Speter continue; 83438032Speter } 83538032Speter else if (c != '!' || state == QST) 83638032Speter { 837112810Sgshapiro /* see if there is room */ 838112810Sgshapiro if (q >= &pvpbuf[pvpbsize - 5]) 839112810Sgshapiro goto addrtoolong; 84038032Speter *q++ = '\\'; 84138032Speter continue; 84238032Speter } 84338032Speter } 84438032Speter 84538032Speter if (c == '\\') 84638032Speter { 84790792Sgshapiro bslashmode = true; 84838032Speter } 84938032Speter else if (state == QST) 85038032Speter { 85164562Sgshapiro /* EMPTY */ 85238032Speter /* do nothing, just avoid next clauses */ 85338032Speter } 85464562Sgshapiro else if (c == '(' && toktab['('] == SPC) 85538032Speter { 85638032Speter cmntcnt++; 85738032Speter c = NOCHAR; 85838032Speter } 85964562Sgshapiro else if (c == ')' && toktab['('] == SPC) 86038032Speter { 86138032Speter if (cmntcnt <= 0) 86238032Speter { 863132943Sgshapiro if (!ignore) 864132943Sgshapiro { 865132943Sgshapiro usrerr("553 Unbalanced ')'"); 866132943Sgshapiro c = NOCHAR; 867132943Sgshapiro } 86838032Speter } 86938032Speter else 87038032Speter cmntcnt--; 87138032Speter } 87238032Speter else if (cmntcnt > 0) 87364562Sgshapiro { 87438032Speter c = NOCHAR; 87564562Sgshapiro } 87638032Speter else if (c == '<') 87738032Speter { 87864562Sgshapiro char *ptr = p; 87938032Speter 88038032Speter anglecnt++; 881363466Sgshapiro while (SM_ISSPACE(*ptr)) 88264562Sgshapiro ptr++; 88364562Sgshapiro if (*ptr == '@') 88490792Sgshapiro route_syntax = true; 88538032Speter } 88638032Speter else if (c == '>') 88738032Speter { 88838032Speter if (anglecnt <= 0) 88938032Speter { 890132943Sgshapiro if (!ignore) 891132943Sgshapiro { 892132943Sgshapiro usrerr("553 Unbalanced '>'"); 893132943Sgshapiro c = NOCHAR; 894132943Sgshapiro } 89538032Speter } 89638032Speter else 89738032Speter anglecnt--; 89890792Sgshapiro route_syntax = false; 89938032Speter } 900363466Sgshapiro else if (delim == ' ' && SM_ISSPACE(c)) 90138032Speter c = ' '; 90238032Speter 90338032Speter if (c == NOCHAR) 90438032Speter continue; 90538032Speter 90638032Speter /* see if this is end of input */ 90738032Speter if (c == delim && anglecnt <= 0 && state != QST) 90838032Speter break; 90938032Speter 91038032Speter newstate = StateTab[state][toktab[c & 0xff]]; 91138032Speter if (tTd(22, 101)) 91290792Sgshapiro sm_dprintf("ns=%02o\n", newstate); 91338032Speter state = newstate & TYPE; 91438032Speter if (state == ILL) 91538032Speter { 91638032Speter if (isascii(c) && isprint(c)) 91790792Sgshapiro usrerr("553 Illegal character %c", c); 91838032Speter else 91990792Sgshapiro usrerr("553 Illegal character 0x%02x", 92090792Sgshapiro c & 0x0ff); 92138032Speter } 92238032Speter if (bitset(M, newstate)) 92338032Speter c = NOCHAR; 92438032Speter if (bitset(B, newstate)) 92538032Speter break; 92638032Speter } 92738032Speter 92838032Speter /* new token */ 92938032Speter if (tok != q) 93038032Speter { 931112810Sgshapiro /* see if there is room */ 932112810Sgshapiro if (q >= &pvpbuf[pvpbsize - 5]) 933112810Sgshapiro goto addrtoolong; 93438032Speter *q++ = '\0'; 93538032Speter if (tTd(22, 36)) 93638032Speter { 93790792Sgshapiro sm_dprintf("tok="); 938132943Sgshapiro xputs(sm_debug_file(), tok); 93990792Sgshapiro sm_dprintf("\n"); 94038032Speter } 94138032Speter if (avp >= &av[MAXATOM]) 94238032Speter { 94364562Sgshapiro usrerr("553 5.1.0 prescan: too many tokens"); 94438032Speter goto returnnull; 94538032Speter } 94638032Speter if (q - tok > MAXNAME) 94738032Speter { 94864562Sgshapiro usrerr("553 5.1.0 prescan: token too long"); 94938032Speter goto returnnull; 95038032Speter } 95138032Speter *avp++ = tok; 95238032Speter } 95338032Speter } while (c != '\0' && (c != delim || anglecnt > 0)); 95438032Speter *avp = NULL; 95538032Speter if (delimptr != NULL) 956120256Sgshapiro { 957120256Sgshapiro if (p > addr) 958120256Sgshapiro p--; 95938032Speter *delimptr = p; 960120256Sgshapiro } 96138032Speter if (tTd(22, 12)) 96238032Speter { 96390792Sgshapiro sm_dprintf("prescan==>"); 964132943Sgshapiro printav(sm_debug_file(), av); 96538032Speter } 96638032Speter CurEnv->e_to = saveto; 96738032Speter if (av[0] == NULL) 96838032Speter { 96938032Speter if (tTd(22, 1)) 97090792Sgshapiro sm_dprintf("prescan: null leading token\n"); 97164562Sgshapiro return NULL; 97238032Speter } 97364562Sgshapiro return av; 97438032Speter} 97590792Sgshapiro/* 97638032Speter** REWRITE -- apply rewrite rules to token vector. 97738032Speter** 97838032Speter** This routine is an ordered production system. Each rewrite 97938032Speter** rule has a LHS (called the pattern) and a RHS (called the 98038032Speter** rewrite); 'rwr' points the the current rewrite rule. 98138032Speter** 98238032Speter** For each rewrite rule, 'avp' points the address vector we 98338032Speter** are trying to match against, and 'pvp' points to the pattern. 98438032Speter** If pvp points to a special match value (MATCHZANY, MATCHANY, 98538032Speter** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 98638032Speter** matched is saved away in the match vector (pointed to by 'mvp'). 98738032Speter** 98838032Speter** When a match between avp & pvp does not match, we try to 98938032Speter** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 99038032Speter** we must also back out the match in mvp. If we reach a 99138032Speter** MATCHANY or MATCHZANY we just extend the match and start 99238032Speter** over again. 99338032Speter** 99438032Speter** When we finally match, we rewrite the address vector 99538032Speter** and try over again. 99638032Speter** 99738032Speter** Parameters: 99838032Speter** pvp -- pointer to token vector. 99938032Speter** ruleset -- the ruleset to use for rewriting. 100038032Speter** reclevel -- recursion level (to catch loops). 100138032Speter** e -- the current envelope. 100290792Sgshapiro** maxatom -- maximum length of buffer (usually MAXATOM) 100338032Speter** 100438032Speter** Returns: 100538032Speter** A status code. If EX_TEMPFAIL, higher level code should 100638032Speter** attempt recovery. 100738032Speter** 100838032Speter** Side Effects: 100938032Speter** pvp is modified. 101038032Speter*/ 101138032Speter 101238032Speterstruct match 101338032Speter{ 101464562Sgshapiro char **match_first; /* first token matched */ 101564562Sgshapiro char **match_last; /* last token matched */ 101664562Sgshapiro char **match_pattern; /* pointer to pattern */ 101738032Speter}; 101838032Speter 101938032Speterint 102090792Sgshapirorewrite(pvp, ruleset, reclevel, e, maxatom) 102138032Speter char **pvp; 102238032Speter int ruleset; 102338032Speter int reclevel; 102438032Speter register ENVELOPE *e; 102590792Sgshapiro int maxatom; 102638032Speter{ 102738032Speter register char *ap; /* address pointer */ 102838032Speter register char *rp; /* rewrite pointer */ 102964562Sgshapiro register char *rulename; /* ruleset name */ 103064562Sgshapiro register char *prefix; 103138032Speter register char **avp; /* address vector pointer */ 103238032Speter register char **rvp; /* rewrite vector pointer */ 103338032Speter register struct match *mlp; /* cur ptr into mlist */ 103438032Speter register struct rewrite *rwr; /* pointer to current rewrite rule */ 103538032Speter int ruleno; /* current rule number */ 103638032Speter int rstat = EX_OK; /* return status */ 103738032Speter int loopcount; 103838032Speter struct match mlist[MAXMATCH]; /* stores match on LHS */ 103964562Sgshapiro char *npvp[MAXATOM + 1]; /* temporary space for rebuild */ 104038032Speter char buf[MAXLINE]; 104164562Sgshapiro char name[6]; 104238032Speter 1043120256Sgshapiro /* 1044120256Sgshapiro ** mlp will not exceed mlist[] because readcf enforces 1045120256Sgshapiro ** the upper limit of entries when reading rulesets. 1046120256Sgshapiro */ 1047120256Sgshapiro 104864562Sgshapiro if (ruleset < 0 || ruleset >= MAXRWSETS) 104938032Speter { 105064562Sgshapiro syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset); 105164562Sgshapiro return EX_CONFIG; 105264562Sgshapiro } 105364562Sgshapiro rulename = RuleSetNames[ruleset]; 105464562Sgshapiro if (rulename == NULL) 105564562Sgshapiro { 1056168515Sgshapiro (void) sm_snprintf(name, sizeof(name), "%d", ruleset); 105764562Sgshapiro rulename = name; 105864562Sgshapiro } 105964562Sgshapiro if (OpMode == MD_TEST) 106064562Sgshapiro prefix = ""; 106164562Sgshapiro else 106264562Sgshapiro prefix = "rewrite: ruleset "; 106364562Sgshapiro if (OpMode == MD_TEST) 106464562Sgshapiro { 106590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 106690792Sgshapiro "%s%-16.16s input:", prefix, rulename); 1067132943Sgshapiro printav(smioout, pvp); 106838032Speter } 106964562Sgshapiro else if (tTd(21, 1)) 107038032Speter { 107190792Sgshapiro sm_dprintf("%s%-16.16s input:", prefix, rulename); 1072132943Sgshapiro printav(sm_debug_file(), pvp); 107338032Speter } 107438032Speter if (reclevel++ > MaxRuleRecursion) 107538032Speter { 107664562Sgshapiro syserr("rewrite: excessive recursion (max %d), ruleset %s", 107764562Sgshapiro MaxRuleRecursion, rulename); 107838032Speter return EX_CONFIG; 107938032Speter } 108038032Speter if (pvp == NULL) 108138032Speter return EX_USAGE; 1082120256Sgshapiro if (maxatom <= 0) 1083120256Sgshapiro return EX_USAGE; 108438032Speter 108538032Speter /* 108638032Speter ** Run through the list of rewrite rules, applying 108738032Speter ** any that match. 108838032Speter */ 108938032Speter 109038032Speter ruleno = 1; 109138032Speter loopcount = 0; 109238032Speter for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 109338032Speter { 109464562Sgshapiro int status; 109538032Speter 109638032Speter /* if already canonical, quit now */ 109738032Speter if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET) 109838032Speter break; 109938032Speter 110038032Speter if (tTd(21, 12)) 110138032Speter { 110264562Sgshapiro if (tTd(21, 15)) 110390792Sgshapiro sm_dprintf("-----trying rule (line %d):", 110464562Sgshapiro rwr->r_line); 110564562Sgshapiro else 110690792Sgshapiro sm_dprintf("-----trying rule:"); 1107132943Sgshapiro printav(sm_debug_file(), rwr->r_lhs); 110838032Speter } 110938032Speter 111038032Speter /* try to match on this rule */ 111138032Speter mlp = mlist; 111238032Speter rvp = rwr->r_lhs; 111338032Speter avp = pvp; 111438032Speter if (++loopcount > 100) 111538032Speter { 111664562Sgshapiro syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d", 111764562Sgshapiro rulename, ruleno); 111838032Speter if (tTd(21, 1)) 111938032Speter { 112090792Sgshapiro sm_dprintf("workspace: "); 1121132943Sgshapiro printav(sm_debug_file(), pvp); 112238032Speter } 112338032Speter break; 112438032Speter } 112538032Speter 112638032Speter while ((ap = *avp) != NULL || *rvp != NULL) 112738032Speter { 112838032Speter rp = *rvp; 112938032Speter if (tTd(21, 35)) 113038032Speter { 113190792Sgshapiro sm_dprintf("ADVANCE rp="); 1132132943Sgshapiro xputs(sm_debug_file(), rp); 113390792Sgshapiro sm_dprintf(", ap="); 1134132943Sgshapiro xputs(sm_debug_file(), ap); 113590792Sgshapiro sm_dprintf("\n"); 113638032Speter } 113738032Speter if (rp == NULL) 113838032Speter { 113938032Speter /* end-of-pattern before end-of-address */ 114038032Speter goto backup; 114138032Speter } 1142168515Sgshapiro if (ap == NULL && 1143168515Sgshapiro (rp[0] & 0377) != MATCHZANY && 1144168515Sgshapiro (rp[0] & 0377) != MATCHZERO) 114538032Speter { 114638032Speter /* end-of-input with patterns left */ 114738032Speter goto backup; 114838032Speter } 114938032Speter 1150168515Sgshapiro switch (rp[0] & 0377) 115138032Speter { 115238032Speter case MATCHCLASS: 115338032Speter /* match any phrase in a class */ 115464562Sgshapiro mlp->match_pattern = rvp; 115564562Sgshapiro mlp->match_first = avp; 115638032Speter extendclass: 115738032Speter ap = *avp; 115838032Speter if (ap == NULL) 115938032Speter goto backup; 116064562Sgshapiro mlp->match_last = avp++; 116164562Sgshapiro cataddr(mlp->match_first, mlp->match_last, 1162168515Sgshapiro buf, sizeof(buf), '\0', true); 116338032Speter if (!wordinclass(buf, rp[1])) 116438032Speter { 116538032Speter if (tTd(21, 36)) 116638032Speter { 116790792Sgshapiro sm_dprintf("EXTEND rp="); 1168132943Sgshapiro xputs(sm_debug_file(), rp); 116990792Sgshapiro sm_dprintf(", ap="); 1170132943Sgshapiro xputs(sm_debug_file(), ap); 117190792Sgshapiro sm_dprintf("\n"); 117238032Speter } 117338032Speter goto extendclass; 117438032Speter } 117538032Speter if (tTd(21, 36)) 117690792Sgshapiro sm_dprintf("CLMATCH\n"); 117738032Speter mlp++; 117838032Speter break; 117938032Speter 118038032Speter case MATCHNCLASS: 118138032Speter /* match any token not in a class */ 118238032Speter if (wordinclass(ap, rp[1])) 118338032Speter goto backup; 118438032Speter 118564562Sgshapiro /* FALLTHROUGH */ 118638032Speter 118738032Speter case MATCHONE: 118838032Speter case MATCHANY: 118938032Speter /* match exactly one token */ 119064562Sgshapiro mlp->match_pattern = rvp; 119164562Sgshapiro mlp->match_first = avp; 119264562Sgshapiro mlp->match_last = avp++; 119338032Speter mlp++; 119438032Speter break; 119538032Speter 119638032Speter case MATCHZANY: 119738032Speter /* match zero or more tokens */ 119864562Sgshapiro mlp->match_pattern = rvp; 119964562Sgshapiro mlp->match_first = avp; 120064562Sgshapiro mlp->match_last = avp - 1; 120138032Speter mlp++; 120238032Speter break; 120338032Speter 120438032Speter case MATCHZERO: 120538032Speter /* match zero tokens */ 120638032Speter break; 120738032Speter 120838032Speter case MACRODEXPAND: 120938032Speter /* 121038032Speter ** Match against run-time macro. 121138032Speter ** This algorithm is broken for the 121238032Speter ** general case (no recursive macros, 121338032Speter ** improper tokenization) but should 121438032Speter ** work for the usual cases. 121538032Speter */ 121638032Speter 121738032Speter ap = macvalue(rp[1], e); 121864562Sgshapiro mlp->match_first = avp; 121938032Speter if (tTd(21, 2)) 122098841Sgshapiro sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n", 122138032Speter macname(rp[1]), 122238032Speter ap == NULL ? "(NULL)" : ap); 122338032Speter 122438032Speter if (ap == NULL) 122538032Speter break; 122638032Speter while (*ap != '\0') 122738032Speter { 122838032Speter if (*avp == NULL || 122990792Sgshapiro sm_strncasecmp(ap, *avp, 123090792Sgshapiro strlen(*avp)) != 0) 123138032Speter { 123238032Speter /* no match */ 123364562Sgshapiro avp = mlp->match_first; 123438032Speter goto backup; 123538032Speter } 123638032Speter ap += strlen(*avp++); 123738032Speter } 123838032Speter 123938032Speter /* match */ 124038032Speter break; 124138032Speter 124238032Speter default: 124338032Speter /* must have exact match */ 124438032Speter if (sm_strcasecmp(rp, ap)) 124538032Speter goto backup; 124638032Speter avp++; 124738032Speter break; 124838032Speter } 124938032Speter 125038032Speter /* successful match on this token */ 125138032Speter rvp++; 125238032Speter continue; 125338032Speter 125438032Speter backup: 125538032Speter /* match failed -- back up */ 125638032Speter while (--mlp >= mlist) 125738032Speter { 125864562Sgshapiro rvp = mlp->match_pattern; 125938032Speter rp = *rvp; 126064562Sgshapiro avp = mlp->match_last + 1; 126138032Speter ap = *avp; 126238032Speter 126338032Speter if (tTd(21, 36)) 126438032Speter { 126590792Sgshapiro sm_dprintf("BACKUP rp="); 1266132943Sgshapiro xputs(sm_debug_file(), rp); 126790792Sgshapiro sm_dprintf(", ap="); 1268132943Sgshapiro xputs(sm_debug_file(), ap); 126990792Sgshapiro sm_dprintf("\n"); 127038032Speter } 127138032Speter 127238032Speter if (ap == NULL) 127338032Speter { 127438032Speter /* run off the end -- back up again */ 127538032Speter continue; 127638032Speter } 1277168515Sgshapiro 1278168515Sgshapiro if ((rp[0] & 0377) == MATCHANY || 1279168515Sgshapiro (rp[0] & 0377) == MATCHZANY) 128038032Speter { 128138032Speter /* extend binding and continue */ 128264562Sgshapiro mlp->match_last = avp++; 128338032Speter rvp++; 128438032Speter mlp++; 128538032Speter break; 128638032Speter } 1287168515Sgshapiro if ((rp[0] & 0377) == MATCHCLASS) 128838032Speter { 128938032Speter /* extend binding and try again */ 129064562Sgshapiro mlp->match_last = avp; 129138032Speter goto extendclass; 129238032Speter } 129338032Speter } 129438032Speter 129538032Speter if (mlp < mlist) 129638032Speter { 129738032Speter /* total failure to match */ 129838032Speter break; 129938032Speter } 130038032Speter } 130138032Speter 130238032Speter /* 130338032Speter ** See if we successfully matched 130438032Speter */ 130538032Speter 130638032Speter if (mlp < mlist || *rvp != NULL) 130738032Speter { 130838032Speter if (tTd(21, 10)) 130990792Sgshapiro sm_dprintf("----- rule fails\n"); 131038032Speter rwr = rwr->r_next; 131138032Speter ruleno++; 131238032Speter loopcount = 0; 131338032Speter continue; 131438032Speter } 131538032Speter 131638032Speter rvp = rwr->r_rhs; 131738032Speter if (tTd(21, 12)) 131838032Speter { 131990792Sgshapiro sm_dprintf("-----rule matches:"); 1320132943Sgshapiro printav(sm_debug_file(), rvp); 132138032Speter } 132238032Speter 132338032Speter rp = *rvp; 132464562Sgshapiro if (rp != NULL) 132538032Speter { 1326168515Sgshapiro if ((rp[0] & 0377) == CANONUSER) 132764562Sgshapiro { 132864562Sgshapiro rvp++; 132964562Sgshapiro rwr = rwr->r_next; 133064562Sgshapiro ruleno++; 133164562Sgshapiro loopcount = 0; 133264562Sgshapiro } 1333168515Sgshapiro else if ((rp[0] & 0377) == CANONHOST) 133464562Sgshapiro { 133564562Sgshapiro rvp++; 133664562Sgshapiro rwr = NULL; 133764562Sgshapiro } 133838032Speter } 133938032Speter 134038032Speter /* substitute */ 134138032Speter for (avp = npvp; *rvp != NULL; rvp++) 134238032Speter { 134338032Speter register struct match *m; 134438032Speter register char **pp; 134538032Speter 134638032Speter rp = *rvp; 1347168515Sgshapiro if ((rp[0] & 0377) == MATCHREPL) 134838032Speter { 134938032Speter /* substitute from LHS */ 135038032Speter m = &mlist[rp[1] - '1']; 135138032Speter if (m < mlist || m >= mlp) 135238032Speter { 135364562Sgshapiro syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds", 135464562Sgshapiro rulename, rp[1]); 135538032Speter return EX_CONFIG; 135638032Speter } 135738032Speter if (tTd(21, 15)) 135838032Speter { 135990792Sgshapiro sm_dprintf("$%c:", rp[1]); 136064562Sgshapiro pp = m->match_first; 136164562Sgshapiro while (pp <= m->match_last) 136238032Speter { 1363363466Sgshapiro sm_dprintf(" %p=\"", (void *)*pp); 136490792Sgshapiro sm_dflush(); 136590792Sgshapiro sm_dprintf("%s\"", *pp++); 136638032Speter } 136790792Sgshapiro sm_dprintf("\n"); 136838032Speter } 136964562Sgshapiro pp = m->match_first; 137064562Sgshapiro while (pp <= m->match_last) 137138032Speter { 137290792Sgshapiro if (avp >= &npvp[maxatom]) 1373120256Sgshapiro goto toolong; 137438032Speter *avp++ = *pp++; 137538032Speter } 137638032Speter } 137738032Speter else 137838032Speter { 137938032Speter /* some sort of replacement */ 138090792Sgshapiro if (avp >= &npvp[maxatom]) 138138032Speter { 138238032Speter toolong: 138364562Sgshapiro syserr("554 5.3.0 rewrite: expansion too long"); 138490792Sgshapiro if (LogLevel > 9) 138590792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 138690792Sgshapiro "rewrite: expansion too long, ruleset=%s, ruleno=%d", 138790792Sgshapiro rulename, ruleno); 138838032Speter return EX_DATAERR; 138938032Speter } 1390168515Sgshapiro if ((rp[0] & 0377) != MACRODEXPAND) 139138032Speter { 1392168515Sgshapiro /* vanilla replacement from RHS */ 139338032Speter *avp++ = rp; 139438032Speter } 139538032Speter else 139638032Speter { 139798841Sgshapiro /* $&{x} replacement */ 139838032Speter char *mval = macvalue(rp[1], e); 139938032Speter char **xpvp; 1400157001Sgshapiro size_t trsize = 0; 140138032Speter static size_t pvpb1_size = 0; 140238032Speter static char **pvpb1 = NULL; 140338032Speter char pvpbuf[PSBUFSIZE]; 140438032Speter 140538032Speter if (tTd(21, 2)) 140698841Sgshapiro sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n", 140738032Speter macname(rp[1]), 140838032Speter mval == NULL ? "(NULL)" : mval); 140938032Speter if (mval == NULL || *mval == '\0') 141038032Speter continue; 141138032Speter 141238032Speter /* save the remainder of the input */ 141338032Speter for (xpvp = pvp; *xpvp != NULL; xpvp++) 1414168515Sgshapiro trsize += sizeof(*xpvp); 1415157001Sgshapiro if (trsize > pvpb1_size) 141638032Speter { 141738032Speter if (pvpb1 != NULL) 141877349Sgshapiro sm_free(pvpb1); 141990792Sgshapiro pvpb1 = (char **) 142090792Sgshapiro sm_pmalloc_x(trsize); 142138032Speter pvpb1_size = trsize; 142238032Speter } 142338032Speter 142464562Sgshapiro memmove((char *) pvpb1, 142564562Sgshapiro (char *) pvp, 142664562Sgshapiro trsize); 142738032Speter 142838032Speter /* scan the new replacement */ 142938032Speter xpvp = prescan(mval, '\0', pvpbuf, 1430168515Sgshapiro sizeof(pvpbuf), NULL, 1431132943Sgshapiro NULL, false); 143238032Speter if (xpvp == NULL) 143338032Speter { 143438032Speter /* prescan pre-printed error */ 143538032Speter return EX_DATAERR; 143638032Speter } 143738032Speter 143838032Speter /* insert it into the output stream */ 143938032Speter while (*xpvp != NULL) 144038032Speter { 144138032Speter if (tTd(21, 19)) 144290792Sgshapiro sm_dprintf(" ... %s\n", 144364562Sgshapiro *xpvp); 144490792Sgshapiro *avp++ = sm_rpool_strdup_x( 144590792Sgshapiro e->e_rpool, *xpvp); 144690792Sgshapiro if (avp >= &npvp[maxatom]) 144738032Speter goto toolong; 144838032Speter xpvp++; 144938032Speter } 145038032Speter if (tTd(21, 19)) 145190792Sgshapiro sm_dprintf(" ... DONE\n"); 145238032Speter 145338032Speter /* restore the old trailing input */ 145464562Sgshapiro memmove((char *) pvp, 145564562Sgshapiro (char *) pvpb1, 145664562Sgshapiro trsize); 145738032Speter } 145838032Speter } 145938032Speter } 146038032Speter *avp++ = NULL; 146138032Speter 146238032Speter /* 146338032Speter ** Check for any hostname/keyword lookups. 146438032Speter */ 146538032Speter 146638032Speter for (rvp = npvp; *rvp != NULL; rvp++) 146738032Speter { 146838032Speter char **hbrvp; 146938032Speter char **xpvp; 1470157001Sgshapiro size_t trsize; 147138032Speter char *replac; 147238032Speter int endtoken; 1473182352Sgshapiro bool external; 147438032Speter STAB *map; 147538032Speter char *mapname; 147638032Speter char **key_rvp; 147738032Speter char **arg_rvp; 147838032Speter char **default_rvp; 1479157001Sgshapiro char cbuf[MAXKEY]; 148038032Speter char *pvpb1[MAXATOM + 1]; 1481120256Sgshapiro char *argvect[MAX_MAP_ARGS]; 148238032Speter char pvpbuf[PSBUFSIZE]; 148338032Speter char *nullpvp[1]; 148438032Speter 148538032Speter hbrvp = rvp; 1486168515Sgshapiro if ((rvp[0][0] & 0377) == HOSTBEGIN) 148738032Speter { 148838032Speter endtoken = HOSTEND; 148938032Speter mapname = "host"; 149038032Speter } 1491168515Sgshapiro else if ((rvp[0][0] & 0377) == LOOKUPBEGIN) 149238032Speter { 149338032Speter endtoken = LOOKUPEND; 149438032Speter mapname = *++rvp; 1495120256Sgshapiro if (mapname == NULL) 1496159609Sgshapiro { 1497120256Sgshapiro syserr("554 5.3.0 rewrite: missing mapname"); 1498159609Sgshapiro /* NOTREACHED */ 1499159609Sgshapiro SM_ASSERT(0); 1500159609Sgshapiro } 150138032Speter } 1502168515Sgshapiro else 1503168515Sgshapiro continue; 1504168515Sgshapiro 1505168515Sgshapiro /* 1506168515Sgshapiro ** Got a hostname/keyword lookup. 1507168515Sgshapiro ** 1508168515Sgshapiro ** This could be optimized fairly easily. 1509168515Sgshapiro */ 1510168515Sgshapiro 151138032Speter map = stab(mapname, ST_MAP, ST_FIND); 151238032Speter if (map == NULL) 1513120256Sgshapiro syserr("554 5.3.0 rewrite: map %s not found", 1514120256Sgshapiro mapname); 151538032Speter 151638032Speter /* extract the match part */ 151738032Speter key_rvp = ++rvp; 1518120256Sgshapiro if (key_rvp == NULL) 1519159609Sgshapiro { 1520120256Sgshapiro syserr("554 5.3.0 rewrite: missing key for map %s", 1521120256Sgshapiro mapname); 1522159609Sgshapiro /* NOTREACHED */ 1523159609Sgshapiro SM_ASSERT(0); 1524159609Sgshapiro } 152538032Speter default_rvp = NULL; 152638032Speter arg_rvp = argvect; 152738032Speter xpvp = NULL; 152838032Speter replac = pvpbuf; 1529168515Sgshapiro while (*rvp != NULL && ((rvp[0][0] & 0377) != endtoken)) 153038032Speter { 1531168515Sgshapiro int nodetype = rvp[0][0] & 0377; 153238032Speter 1533120256Sgshapiro if (nodetype != CANONHOST && 1534120256Sgshapiro nodetype != CANONUSER) 153538032Speter { 153638032Speter rvp++; 153738032Speter continue; 153838032Speter } 153938032Speter 154038032Speter *rvp++ = NULL; 154138032Speter 154238032Speter if (xpvp != NULL) 154338032Speter { 154438032Speter cataddr(xpvp, NULL, replac, 1545168515Sgshapiro &pvpbuf[sizeof(pvpbuf)] - replac, 1546168515Sgshapiro '\0', false); 1547120256Sgshapiro if (arg_rvp < 1548120256Sgshapiro &argvect[MAX_MAP_ARGS - 1]) 1549120256Sgshapiro *++arg_rvp = replac; 155038032Speter replac += strlen(replac) + 1; 155138032Speter xpvp = NULL; 155238032Speter } 155338032Speter switch (nodetype) 155438032Speter { 155538032Speter case CANONHOST: 155638032Speter xpvp = rvp; 155738032Speter break; 155838032Speter 155938032Speter case CANONUSER: 156038032Speter default_rvp = rvp; 156138032Speter break; 156238032Speter } 156338032Speter } 156438032Speter if (*rvp != NULL) 156538032Speter *rvp++ = NULL; 156638032Speter if (xpvp != NULL) 156738032Speter { 156838032Speter cataddr(xpvp, NULL, replac, 1569168515Sgshapiro &pvpbuf[sizeof(pvpbuf)] - replac, 1570168515Sgshapiro '\0', false); 1571120256Sgshapiro if (arg_rvp < &argvect[MAX_MAP_ARGS - 1]) 1572120256Sgshapiro *++arg_rvp = replac; 157338032Speter } 1574120256Sgshapiro if (arg_rvp >= &argvect[MAX_MAP_ARGS - 1]) 1575120256Sgshapiro argvect[MAX_MAP_ARGS - 1] = NULL; 1576120256Sgshapiro else 1577120256Sgshapiro *++arg_rvp = NULL; 157838032Speter 157938032Speter /* save the remainder of the input string */ 1580168515Sgshapiro trsize = (avp - rvp + 1) * sizeof(*rvp); 158164562Sgshapiro memmove((char *) pvpb1, (char *) rvp, trsize); 158238032Speter 158338032Speter /* look it up */ 1584168515Sgshapiro cataddr(key_rvp, NULL, cbuf, sizeof(cbuf), 1585168515Sgshapiro map == NULL ? '\0' : map->s_map.map_spacesub, 1586168515Sgshapiro true); 158764562Sgshapiro argvect[0] = cbuf; 158864562Sgshapiro replac = map_lookup(map, cbuf, argvect, &rstat, e); 1589182352Sgshapiro external = replac != NULL; 159038032Speter 159138032Speter /* if no replacement, use default */ 159238032Speter if (replac == NULL && default_rvp != NULL) 159338032Speter { 159438032Speter /* create the default */ 1595168515Sgshapiro cataddr(default_rvp, NULL, cbuf, sizeof(cbuf), 1596168515Sgshapiro '\0', false); 159764562Sgshapiro replac = cbuf; 159838032Speter } 159938032Speter 160038032Speter if (replac == NULL) 160138032Speter { 160238032Speter xpvp = key_rvp; 160338032Speter } 160438032Speter else if (*replac == '\0') 160538032Speter { 160638032Speter /* null replacement */ 160738032Speter nullpvp[0] = NULL; 160838032Speter xpvp = nullpvp; 160938032Speter } 161038032Speter else 161138032Speter { 161238032Speter /* scan the new replacement */ 161338032Speter xpvp = prescan(replac, '\0', pvpbuf, 1614182352Sgshapiro sizeof(pvpbuf), NULL, 1615182352Sgshapiro external ? NULL : IntTokenTab, 1616168515Sgshapiro false); 161738032Speter if (xpvp == NULL) 161838032Speter { 161938032Speter /* prescan already printed error */ 162038032Speter return EX_DATAERR; 162138032Speter } 162238032Speter } 162338032Speter 162438032Speter /* append it to the token list */ 162538032Speter for (avp = hbrvp; *xpvp != NULL; xpvp++) 162638032Speter { 162790792Sgshapiro *avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp); 162890792Sgshapiro if (avp >= &npvp[maxatom]) 162938032Speter goto toolong; 163038032Speter } 163138032Speter 163238032Speter /* restore the old trailing information */ 163338032Speter rvp = avp - 1; 163438032Speter for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 163590792Sgshapiro if (avp >= &npvp[maxatom]) 163638032Speter goto toolong; 163738032Speter } 163838032Speter 163938032Speter /* 164038032Speter ** Check for subroutine calls. 164138032Speter */ 164238032Speter 164364562Sgshapiro status = callsubr(npvp, reclevel, e); 164464562Sgshapiro if (rstat == EX_OK || status == EX_TEMPFAIL) 164564562Sgshapiro rstat = status; 164638032Speter 164738032Speter /* copy vector back into original space. */ 164838032Speter for (avp = npvp; *avp++ != NULL;) 164938032Speter continue; 165064562Sgshapiro memmove((char *) pvp, (char *) npvp, 1651168515Sgshapiro (int) (avp - npvp) * sizeof(*avp)); 165264562Sgshapiro 165338032Speter if (tTd(21, 4)) 165438032Speter { 165590792Sgshapiro sm_dprintf("rewritten as:"); 1656132943Sgshapiro printav(sm_debug_file(), pvp); 165738032Speter } 165838032Speter } 165938032Speter 166064562Sgshapiro if (OpMode == MD_TEST) 166138032Speter { 166290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 166390792Sgshapiro "%s%-16.16s returns:", prefix, rulename); 1664132943Sgshapiro printav(smioout, pvp); 166538032Speter } 166664562Sgshapiro else if (tTd(21, 1)) 166764562Sgshapiro { 166890792Sgshapiro sm_dprintf("%s%-16.16s returns:", prefix, rulename); 1669132943Sgshapiro printav(sm_debug_file(), pvp); 167064562Sgshapiro } 167138032Speter return rstat; 167238032Speter} 167390792Sgshapiro/* 167438032Speter** CALLSUBR -- call subroutines in rewrite vector 167538032Speter** 167638032Speter** Parameters: 167738032Speter** pvp -- pointer to token vector. 167838032Speter** reclevel -- the current recursion level. 167938032Speter** e -- the current envelope. 168038032Speter** 168138032Speter** Returns: 168238032Speter** The status from the subroutine call. 168338032Speter** 168438032Speter** Side Effects: 168538032Speter** pvp is modified. 168638032Speter*/ 168738032Speter 168864562Sgshapirostatic int 168938032Spetercallsubr(pvp, reclevel, e) 169038032Speter char **pvp; 169138032Speter int reclevel; 169238032Speter ENVELOPE *e; 169338032Speter{ 169464562Sgshapiro char **avp; 169538032Speter register int i; 169690792Sgshapiro int subr, j; 169790792Sgshapiro int nsubr; 169864562Sgshapiro int status; 169938032Speter int rstat = EX_OK; 170090792Sgshapiro#define MAX_SUBR 16 170190792Sgshapiro int subrnumber[MAX_SUBR]; 170290792Sgshapiro int subrindex[MAX_SUBR]; 170338032Speter 170490792Sgshapiro nsubr = 0; 170590792Sgshapiro 170690792Sgshapiro /* 170790792Sgshapiro ** Look for subroutine calls in pvp, collect them into subr*[] 170890792Sgshapiro ** We will perform the calls in the next loop, because we will 170990792Sgshapiro ** call the "last" subroutine first to avoid recursive calls 171090792Sgshapiro ** and too much copying. 171190792Sgshapiro */ 171290792Sgshapiro 171390792Sgshapiro for (avp = pvp, j = 0; *avp != NULL; avp++, j++) 171438032Speter { 1715168515Sgshapiro if ((avp[0][0] & 0377) == CALLSUBR && avp[1] != NULL) 171638032Speter { 171738032Speter stripquotes(avp[1]); 171838032Speter subr = strtorwset(avp[1], NULL, ST_FIND); 171938032Speter if (subr < 0) 172038032Speter { 172190792Sgshapiro syserr("554 5.3.5 Unknown ruleset %s", avp[1]); 172238032Speter return EX_CONFIG; 172338032Speter } 172438032Speter 172538032Speter /* 172690792Sgshapiro ** XXX instead of doing this we could optimize 172790792Sgshapiro ** the rules after reading them: just remove 172890792Sgshapiro ** calls to empty rulesets 172938032Speter */ 173038032Speter 173190792Sgshapiro /* subroutine is an empty ruleset? don't call it */ 173290792Sgshapiro if (RewriteRules[subr] == NULL) 173390792Sgshapiro { 173490792Sgshapiro if (tTd(21, 3)) 173590792Sgshapiro sm_dprintf("-----skip subr %s (%d)\n", 173690792Sgshapiro avp[1], subr); 173790792Sgshapiro for (i = 2; avp[i] != NULL; i++) 173890792Sgshapiro avp[i - 2] = avp[i]; 173990792Sgshapiro avp[i - 2] = NULL; 174038032Speter continue; 174190792Sgshapiro } 174290792Sgshapiro if (++nsubr >= MAX_SUBR) 174338032Speter { 174490792Sgshapiro syserr("554 5.3.0 Too many subroutine calls (%d max)", 174590792Sgshapiro MAX_SUBR); 174690792Sgshapiro return EX_CONFIG; 174738032Speter } 174890792Sgshapiro subrnumber[nsubr] = subr; 174990792Sgshapiro subrindex[nsubr] = j; 175090792Sgshapiro } 175190792Sgshapiro } 175238032Speter 175390792Sgshapiro /* 175490792Sgshapiro ** Perform the actual subroutines calls, "last" one first, i.e., 175590792Sgshapiro ** go from the right to the left through all calls, 175690792Sgshapiro ** do the rewriting in place. 175790792Sgshapiro */ 175838032Speter 175990792Sgshapiro for (; nsubr > 0; nsubr--) 176090792Sgshapiro { 176190792Sgshapiro subr = subrnumber[nsubr]; 176290792Sgshapiro avp = pvp + subrindex[nsubr]; 176338032Speter 176490792Sgshapiro /* remove the subroutine call and name */ 176590792Sgshapiro for (i = 2; avp[i] != NULL; i++) 176690792Sgshapiro avp[i - 2] = avp[i]; 176790792Sgshapiro avp[i - 2] = NULL; 176838032Speter 176990792Sgshapiro /* 177090792Sgshapiro ** Now we need to call the ruleset specified for 1771120256Sgshapiro ** the subroutine. We can do this in place since 177290792Sgshapiro ** we call the "last" subroutine first. 177390792Sgshapiro */ 177490792Sgshapiro 177590792Sgshapiro status = rewrite(avp, subr, reclevel, e, 177690792Sgshapiro MAXATOM - subrindex[nsubr]); 177790792Sgshapiro if (status != EX_OK && status != EX_TEMPFAIL) 177890792Sgshapiro return status; 177990792Sgshapiro if (rstat == EX_OK || status == EX_TEMPFAIL) 178090792Sgshapiro rstat = status; 178138032Speter } 178238032Speter return rstat; 178338032Speter} 178490792Sgshapiro/* 178538032Speter** MAP_LOOKUP -- do lookup in map 178638032Speter** 178738032Speter** Parameters: 178890792Sgshapiro** smap -- the map to use for the lookup. 178938032Speter** key -- the key to look up. 179038032Speter** argvect -- arguments to pass to the map lookup. 179138032Speter** pstat -- a pointer to an integer in which to store the 179238032Speter** status from the lookup. 179338032Speter** e -- the current envelope. 179438032Speter** 179538032Speter** Returns: 179638032Speter** The result of the lookup. 179738032Speter** NULL -- if there was no data for the given key. 179838032Speter*/ 179938032Speter 180064562Sgshapirostatic char * 180164562Sgshapiromap_lookup(smap, key, argvect, pstat, e) 180264562Sgshapiro STAB *smap; 180338032Speter char key[]; 180438032Speter char **argvect; 180538032Speter int *pstat; 180638032Speter ENVELOPE *e; 180738032Speter{ 180864562Sgshapiro auto int status = EX_OK; 180964562Sgshapiro MAP *map; 181038032Speter char *replac; 181138032Speter 181264562Sgshapiro if (smap == NULL) 181364562Sgshapiro return NULL; 181464562Sgshapiro 181564562Sgshapiro map = &smap->s_map; 181664562Sgshapiro DYNOPENMAP(map); 1817363466Sgshapiro map->map_mflags |= MF_SECURE; /* default: secure */ 181864562Sgshapiro 181964562Sgshapiro if (e->e_sendmode == SM_DEFER && 182064562Sgshapiro bitset(MF_DEFER, map->map_mflags)) 182138032Speter { 182238032Speter /* don't do any map lookups */ 182338032Speter if (tTd(60, 1)) 182490792Sgshapiro sm_dprintf("map_lookup(%s, %s) => DEFERRED\n", 182564562Sgshapiro smap->s_name, key); 182638032Speter *pstat = EX_TEMPFAIL; 182738032Speter return NULL; 182838032Speter } 182938032Speter 183064562Sgshapiro if (!bitset(MF_KEEPQUOTES, map->map_mflags)) 183138032Speter stripquotes(key); 183238032Speter 183338032Speter if (tTd(60, 1)) 183442575Speter { 1835168515Sgshapiro sm_dprintf("map_lookup(%s, ", smap->s_name); 1836168515Sgshapiro xputs(sm_debug_file(), key); 183742575Speter if (tTd(60, 5)) 183842575Speter { 183942575Speter int i; 184042575Speter 184142575Speter for (i = 0; argvect[i] != NULL; i++) 184290792Sgshapiro sm_dprintf(", %%%d=%s", i, argvect[i]); 184342575Speter } 184490792Sgshapiro sm_dprintf(") => "); 184542575Speter } 184664562Sgshapiro replac = (*map->map_class->map_lookup)(map, key, argvect, &status); 1847363466Sgshapiro if (bitset(MF_SECURE, map->map_mflags)) 1848363466Sgshapiro map->map_mflags &= ~MF_SECURE; 1849363466Sgshapiro else 1850363466Sgshapiro e->e_flags &= ~EF_SECURE; 1851363466Sgshapiro 185238032Speter if (tTd(60, 1)) 1853363466Sgshapiro sm_dprintf("%s (%d), ad=%d\n", 185438032Speter replac != NULL ? replac : "NOT FOUND", 1855363466Sgshapiro status, bitset(MF_SECURE, map->map_mflags)); 185638032Speter 185764562Sgshapiro /* should recover if status == EX_TEMPFAIL */ 185864562Sgshapiro if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags)) 185938032Speter { 186038032Speter *pstat = EX_TEMPFAIL; 186138032Speter if (tTd(60, 1)) 186290792Sgshapiro sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n", 186364562Sgshapiro smap->s_name, key, errno); 186438032Speter if (e->e_message == NULL) 186538032Speter { 186638032Speter char mbuf[320]; 186738032Speter 1868168515Sgshapiro (void) sm_snprintf(mbuf, sizeof(mbuf), 186938032Speter "%.80s map: lookup (%s): deferred", 187064562Sgshapiro smap->s_name, 187138032Speter shortenstring(key, MAXSHORTSTR)); 187290792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf); 187338032Speter } 187438032Speter } 187564562Sgshapiro if (status == EX_TEMPFAIL && map->map_tapp != NULL) 187638032Speter { 187764562Sgshapiro size_t i = strlen(key) + strlen(map->map_tapp) + 1; 187838032Speter static char *rwbuf = NULL; 187938032Speter static size_t rwbuflen = 0; 188038032Speter 188138032Speter if (i > rwbuflen) 188238032Speter { 188338032Speter if (rwbuf != NULL) 188477349Sgshapiro sm_free(rwbuf); 188538032Speter rwbuflen = i; 188690792Sgshapiro rwbuf = (char *) sm_pmalloc_x(rwbuflen); 188738032Speter } 188890792Sgshapiro (void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp); 188938032Speter if (tTd(60, 4)) 189090792Sgshapiro sm_dprintf("map_lookup tempfail: returning \"%s\"\n", 189138032Speter rwbuf); 189238032Speter return rwbuf; 189338032Speter } 189438032Speter return replac; 189538032Speter} 189690792Sgshapiro/* 189764562Sgshapiro** INITERRMAILERS -- initialize error and discard mailers 189864562Sgshapiro** 189964562Sgshapiro** Parameters: 190064562Sgshapiro** none. 190164562Sgshapiro** 190264562Sgshapiro** Returns: 190364562Sgshapiro** none. 190464562Sgshapiro** 190564562Sgshapiro** Side Effects: 190664562Sgshapiro** initializes error and discard mailers. 190764562Sgshapiro*/ 190864562Sgshapiro 190964562Sgshapirostatic MAILER discardmailer; 191064562Sgshapirostatic MAILER errormailer; 191164562Sgshapirostatic char *discardargv[] = { "DISCARD", NULL }; 191264562Sgshapirostatic char *errorargv[] = { "ERROR", NULL }; 191364562Sgshapiro 191464562Sgshapirovoid 191564562Sgshapiroiniterrmailers() 191664562Sgshapiro{ 191764562Sgshapiro if (discardmailer.m_name == NULL) 191864562Sgshapiro { 191964562Sgshapiro /* initialize the discard mailer */ 192064562Sgshapiro discardmailer.m_name = "*discard*"; 192164562Sgshapiro discardmailer.m_mailer = "DISCARD"; 192264562Sgshapiro discardmailer.m_argv = discardargv; 192364562Sgshapiro } 192464562Sgshapiro if (errormailer.m_name == NULL) 192564562Sgshapiro { 192664562Sgshapiro /* initialize the bogus mailer */ 192764562Sgshapiro errormailer.m_name = "*error*"; 192864562Sgshapiro errormailer.m_mailer = "ERROR"; 192964562Sgshapiro errormailer.m_argv = errorargv; 193064562Sgshapiro } 193164562Sgshapiro} 193290792Sgshapiro/* 193338032Speter** BUILDADDR -- build address from token vector. 193438032Speter** 193538032Speter** Parameters: 193638032Speter** tv -- token vector. 193738032Speter** a -- pointer to address descriptor to fill. 193838032Speter** If NULL, one will be allocated. 193938032Speter** flags -- info regarding whether this is a sender or 194038032Speter** a recipient. 194138032Speter** e -- the current envelope. 194238032Speter** 194338032Speter** Returns: 194438032Speter** NULL if there was an error. 194538032Speter** 'a' otherwise. 194638032Speter** 194738032Speter** Side Effects: 194838032Speter** fills in 'a' 194938032Speter*/ 195038032Speter 195164562Sgshapirostatic struct errcodes 195238032Speter{ 195338032Speter char *ec_name; /* name of error code */ 195438032Speter int ec_code; /* numeric code */ 195538032Speter} ErrorCodes[] = 195638032Speter{ 195738032Speter { "usage", EX_USAGE }, 195838032Speter { "nouser", EX_NOUSER }, 195938032Speter { "nohost", EX_NOHOST }, 196038032Speter { "unavailable", EX_UNAVAILABLE }, 196138032Speter { "software", EX_SOFTWARE }, 196238032Speter { "tempfail", EX_TEMPFAIL }, 196338032Speter { "protocol", EX_PROTOCOL }, 196438032Speter { "config", EX_CONFIG }, 196538032Speter { NULL, EX_UNAVAILABLE } 196638032Speter}; 196738032Speter 196864562Sgshapirostatic ADDRESS * 196938032Speterbuildaddr(tv, a, flags, e) 197038032Speter register char **tv; 197138032Speter register ADDRESS *a; 197238032Speter int flags; 197338032Speter register ENVELOPE *e; 197438032Speter{ 197594334Sgshapiro bool tempfail = false; 1976120256Sgshapiro int maxatom; 197738032Speter struct mailer **mp; 197838032Speter register struct mailer *m; 197938032Speter register char *p; 198038032Speter char *mname; 198138032Speter char **hostp; 198238032Speter char hbuf[MAXNAME + 1]; 198342575Speter static char ubuf[MAXNAME + 2]; 198438032Speter 198538032Speter if (tTd(24, 5)) 198638032Speter { 198790792Sgshapiro sm_dprintf("buildaddr, flags=%x, tv=", flags); 1988132943Sgshapiro printav(sm_debug_file(), tv); 198938032Speter } 199038032Speter 1991120256Sgshapiro maxatom = MAXATOM; 199238032Speter if (a == NULL) 1993168515Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a)); 1994168515Sgshapiro memset((char *) a, '\0', sizeof(*a)); 199564562Sgshapiro hbuf[0] = '\0'; 199638032Speter 199738032Speter /* set up default error return flags */ 199838032Speter a->q_flags |= DefaultNotify; 199938032Speter 200038032Speter /* figure out what net/mailer to use */ 200138032Speter if (*tv == NULL || (**tv & 0377) != CANONNET) 200238032Speter { 200364562Sgshapiro syserr("554 5.3.5 buildaddr: no mailer in parsed address"); 200438032Speterbadaddr: 200594334Sgshapiro /* 200694334Sgshapiro ** ExitStat may have been set by an earlier map open 200794334Sgshapiro ** failure (to a permanent error (EX_OSERR) in syserr()) 200894334Sgshapiro ** so we also need to check if this particular $#error 200994334Sgshapiro ** return wanted a 4XX failure. 201094334Sgshapiro ** 201194334Sgshapiro ** XXX the real fix is probably to set ExitStat correctly, 201294334Sgshapiro ** i.e., to EX_TEMPFAIL if the map open is just a temporary 201394334Sgshapiro ** error. 201494334Sgshapiro */ 201594334Sgshapiro 201694334Sgshapiro if (ExitStat == EX_TEMPFAIL || tempfail) 201764562Sgshapiro a->q_state = QS_QUEUEUP; 201864562Sgshapiro else 201938032Speter { 202064562Sgshapiro a->q_state = QS_BADADDR; 202164562Sgshapiro a->q_mailer = &errormailer; 202238032Speter } 202338032Speter return a; 202438032Speter } 202538032Speter mname = *++tv; 2026120256Sgshapiro --maxatom; 202738032Speter 202838032Speter /* extract host and user portions */ 202938032Speter if (*++tv != NULL && (**tv & 0377) == CANONHOST) 2030120256Sgshapiro { 203138032Speter hostp = ++tv; 2032120256Sgshapiro --maxatom; 2033120256Sgshapiro } 203438032Speter else 203538032Speter hostp = NULL; 2036120256Sgshapiro --maxatom; 203738032Speter while (*tv != NULL && (**tv & 0377) != CANONUSER) 2038120256Sgshapiro { 203938032Speter tv++; 2040120256Sgshapiro --maxatom; 2041120256Sgshapiro } 204238032Speter if (*tv == NULL) 204338032Speter { 204464562Sgshapiro syserr("554 5.3.5 buildaddr: no user"); 204538032Speter goto badaddr; 204638032Speter } 204738032Speter if (tv == hostp) 204838032Speter hostp = NULL; 204938032Speter else if (hostp != NULL) 2050168515Sgshapiro cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', false); 2051168515Sgshapiro cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false); 2052120256Sgshapiro --maxatom; 205338032Speter 205438032Speter /* save away the host name */ 205590792Sgshapiro if (sm_strcasecmp(mname, "error") == 0) 205638032Speter { 205764562Sgshapiro /* Set up triplet for use by -bv */ 205864562Sgshapiro a->q_mailer = &errormailer; 205990792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); 206090792Sgshapiro /* XXX wrong place? */ 206164562Sgshapiro 206238032Speter if (hostp != NULL) 206338032Speter { 206438032Speter register struct errcodes *ep; 206538032Speter 206690792Sgshapiro a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); 206738032Speter if (strchr(hbuf, '.') != NULL) 206838032Speter { 206990792Sgshapiro a->q_status = sm_rpool_strdup_x(e->e_rpool, 207090792Sgshapiro hbuf); 207138032Speter setstat(dsntoexitstat(hbuf)); 207238032Speter } 207338032Speter else if (isascii(hbuf[0]) && isdigit(hbuf[0])) 207438032Speter { 207538032Speter setstat(atoi(hbuf)); 207638032Speter } 207738032Speter else 207838032Speter { 207938032Speter for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 208090792Sgshapiro if (sm_strcasecmp(ep->ec_name, hbuf) == 0) 208138032Speter break; 208238032Speter setstat(ep->ec_code); 208338032Speter } 208438032Speter } 208538032Speter else 208664562Sgshapiro { 208764562Sgshapiro a->q_host = NULL; 208838032Speter setstat(EX_UNAVAILABLE); 208964562Sgshapiro } 209038032Speter stripquotes(ubuf); 209164562Sgshapiro if (ISSMTPCODE(ubuf) && ubuf[3] == ' ') 209238032Speter { 209364562Sgshapiro char fmt[16]; 209464562Sgshapiro int off; 209538032Speter 209664562Sgshapiro if ((off = isenhsc(ubuf + 4, ' ')) > 0) 209738032Speter { 209864562Sgshapiro ubuf[off + 4] = '\0'; 209964562Sgshapiro off += 5; 210038032Speter } 210164562Sgshapiro else 210264562Sgshapiro { 210364562Sgshapiro off = 4; 210464562Sgshapiro ubuf[3] = '\0'; 210564562Sgshapiro } 2106168515Sgshapiro (void) sm_strlcpyn(fmt, sizeof(fmt), 2, ubuf, " %s"); 210764562Sgshapiro if (off > 4) 210864562Sgshapiro usrerr(fmt, ubuf + off); 210964562Sgshapiro else if (isenhsc(hbuf, '\0') > 0) 211064562Sgshapiro usrerrenh(hbuf, fmt, ubuf + off); 211164562Sgshapiro else 211264562Sgshapiro usrerr(fmt, ubuf + off); 211364562Sgshapiro /* XXX ubuf[off - 1] = ' '; */ 211494334Sgshapiro if (ubuf[0] == '4') 211594334Sgshapiro tempfail = true; 211638032Speter } 211738032Speter else 211838032Speter { 211964562Sgshapiro usrerr("553 5.3.0 %s", ubuf); 212038032Speter } 212138032Speter goto badaddr; 212238032Speter } 212338032Speter 212438032Speter for (mp = Mailer; (m = *mp++) != NULL; ) 212538032Speter { 212690792Sgshapiro if (sm_strcasecmp(m->m_name, mname) == 0) 212738032Speter break; 212838032Speter } 212938032Speter if (m == NULL) 213038032Speter { 213164562Sgshapiro syserr("554 5.3.5 buildaddr: unknown mailer %s", mname); 213238032Speter goto badaddr; 213338032Speter } 213438032Speter a->q_mailer = m; 213538032Speter 213638032Speter /* figure out what host (if any) */ 213738032Speter if (hostp == NULL) 213838032Speter { 213938032Speter if (!bitnset(M_LOCALMAILER, m->m_flags)) 214038032Speter { 214164562Sgshapiro syserr("554 5.3.5 buildaddr: no host"); 214238032Speter goto badaddr; 214338032Speter } 214438032Speter a->q_host = NULL; 214538032Speter } 214638032Speter else 214790792Sgshapiro a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); 214838032Speter 214938032Speter /* figure out the user */ 215038032Speter p = ubuf; 215138032Speter if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@') 215238032Speter { 215338032Speter p++; 215438032Speter tv++; 2155120256Sgshapiro --maxatom; 215638032Speter a->q_flags |= QNOTREMOTE; 215738032Speter } 215838032Speter 215938032Speter /* do special mapping for local mailer */ 216038032Speter if (*p == '"') 216138032Speter p++; 216238032Speter if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags)) 216338032Speter a->q_mailer = m = ProgMailer; 216438032Speter else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags)) 216538032Speter a->q_mailer = m = FileMailer; 216638032Speter else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags)) 216738032Speter { 216838032Speter /* may be :include: */ 216938032Speter stripquotes(ubuf); 217090792Sgshapiro if (sm_strncasecmp(ubuf, ":include:", 9) == 0) 217138032Speter { 217238032Speter /* if :include:, don't need further rewriting */ 217338032Speter a->q_mailer = m = InclMailer; 217490792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]); 217538032Speter return a; 217638032Speter } 217738032Speter } 217838032Speter 217938032Speter /* rewrite according recipient mailer rewriting rules */ 218090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', a->q_host); 218164562Sgshapiro 218290792Sgshapiro if (ConfigLevel >= 10 || 218364562Sgshapiro !bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) 218438032Speter { 218538032Speter /* sender addresses done later */ 2186120256Sgshapiro (void) rewrite(tv, 2, 0, e, maxatom); 218738032Speter if (m->m_re_rwset > 0) 2188120256Sgshapiro (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom); 218938032Speter } 2190120256Sgshapiro (void) rewrite(tv, 4, 0, e, maxatom); 219138032Speter 219238032Speter /* save the result for the command line/RCPT argument */ 2193168515Sgshapiro cataddr(tv, NULL, ubuf, sizeof(ubuf), '\0', true); 219490792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); 219538032Speter 219638032Speter /* 219738032Speter ** Do mapping to lower case as requested by mailer 219838032Speter */ 219938032Speter 220038032Speter if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags)) 220138032Speter makelower(a->q_host); 220238032Speter if (!bitnset(M_USR_UPPER, m->m_flags)) 220338032Speter makelower(a->q_user); 220438032Speter 220538032Speter if (tTd(24, 6)) 220638032Speter { 220790792Sgshapiro sm_dprintf("buildaddr => "); 2208132943Sgshapiro printaddr(sm_debug_file(), a, false); 220938032Speter } 221038032Speter return a; 221138032Speter} 2212110560Sgshapiro 221390792Sgshapiro/* 221438032Speter** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 221538032Speter** 221638032Speter** Parameters: 221738032Speter** pvp -- parameter vector to rebuild. 221838032Speter** evp -- last parameter to include. Can be NULL to 221938032Speter** use entire pvp. 222038032Speter** buf -- buffer to build the string into. 222138032Speter** sz -- size of buf. 2222285229Sgshapiro** spacesub -- the space separator character; 2223285229Sgshapiro** '\0': SpaceSub. 2224285229Sgshapiro** NOSPACESEP: no separator 2225168515Sgshapiro** external -- convert to external form? 2226168515Sgshapiro** (no metacharacters; METAQUOTEs removed, see below) 222738032Speter** 222838032Speter** Returns: 222938032Speter** none. 223038032Speter** 223138032Speter** Side Effects: 223238032Speter** Destroys buf. 2233168515Sgshapiro** 2234168515Sgshapiro** Notes: 2235168515Sgshapiro** There are two formats for strings: internal and external. 2236168515Sgshapiro** The external format is just an eight-bit clean string (no 2237168515Sgshapiro** null bytes, everything else OK). The internal format can 2238168515Sgshapiro** include sendmail metacharacters. The special character 2239168515Sgshapiro** METAQUOTE essentially quotes the character following, stripping 2240168515Sgshapiro** it of all special semantics. 2241168515Sgshapiro** 2242168515Sgshapiro** The cataddr routine needs to be aware of whether it is producing 2243168515Sgshapiro** an internal or external form as output (it only takes internal 2244168515Sgshapiro** form as input). 2245168515Sgshapiro** 2246168515Sgshapiro** The parseaddr routine has a similar issue on input, but that 2247168515Sgshapiro** is flagged on the basis of which token table is passed in. 224838032Speter*/ 224938032Speter 225038032Spetervoid 2251168515Sgshapirocataddr(pvp, evp, buf, sz, spacesub, external) 225238032Speter char **pvp; 225338032Speter char **evp; 225438032Speter char *buf; 225538032Speter register int sz; 225638032Speter int spacesub; 2257168515Sgshapiro bool external; 225838032Speter{ 2259168515Sgshapiro bool oatomtok, natomtok; 2260168515Sgshapiro char *p; 226138032Speter 2262168515Sgshapiro oatomtok = natomtok = false; 2263168515Sgshapiro if (tTd(59, 14)) 2264168515Sgshapiro { 2265168515Sgshapiro sm_dprintf("cataddr(%d) <==", external); 2266168515Sgshapiro printav(sm_debug_file(), pvp); 2267168515Sgshapiro } 2268168515Sgshapiro 226964562Sgshapiro if (sz <= 0) 227064562Sgshapiro return; 227164562Sgshapiro 227238032Speter if (spacesub == '\0') 227338032Speter spacesub = SpaceSub; 227438032Speter 227538032Speter if (pvp == NULL) 227638032Speter { 227764562Sgshapiro *buf = '\0'; 227838032Speter return; 227938032Speter } 228038032Speter p = buf; 228138032Speter sz -= 2; 228290792Sgshapiro while (*pvp != NULL && sz > 0) 228338032Speter { 2284168515Sgshapiro char *q; 2285168515Sgshapiro 2286182352Sgshapiro natomtok = (IntTokenTab[**pvp & 0xff] == ATM); 2287285229Sgshapiro if (oatomtok && natomtok && spacesub != NOSPACESEP) 228864562Sgshapiro { 228938032Speter *p++ = spacesub; 229090792Sgshapiro if (--sz <= 0) 229190792Sgshapiro break; 229264562Sgshapiro } 2293168515Sgshapiro for (q = *pvp; *q != '\0'; ) 2294168515Sgshapiro { 2295168515Sgshapiro int c; 2296168515Sgshapiro 2297168515Sgshapiro if (--sz <= 0) 2298168515Sgshapiro break; 2299168515Sgshapiro *p++ = c = *q++; 2300168515Sgshapiro 2301168515Sgshapiro /* 2302168515Sgshapiro ** If the current character (c) is METAQUOTE and we 2303168515Sgshapiro ** want the "external" form and the next character 2304168515Sgshapiro ** is not NUL, then overwrite METAQUOTE with that 2305363466Sgshapiro ** character (i.e., METAQUOTE ch is changed to ch). 2306363466Sgshapiro ** p[-1] is used because p is advanced (above). 2307168515Sgshapiro */ 2308168515Sgshapiro 2309168515Sgshapiro if ((c & 0377) == METAQUOTE && external && *q != '\0') 2310168515Sgshapiro p[-1] = *q++; 2311168515Sgshapiro } 2312132943Sgshapiro if (sz <= 0) 231390792Sgshapiro break; 231438032Speter oatomtok = natomtok; 231538032Speter if (pvp++ == evp) 231638032Speter break; 231738032Speter } 2318132943Sgshapiro 2319147078Sgshapiro#if 0 2320147078Sgshapiro /* 2321147078Sgshapiro ** Silently truncate long strings: even though this doesn't 2322147078Sgshapiro ** seem like a good idea it is necessary because header checks 2323147078Sgshapiro ** send the whole header value to rscheck() and hence rewrite(). 2324147078Sgshapiro ** The latter however sometimes uses a "short" buffer (e.g., 2325147078Sgshapiro ** cbuf[MAXNAME + 1]) to call cataddr() which then triggers this 2326147078Sgshapiro ** error function. One possible fix to the problem is to pass 2327147078Sgshapiro ** flags to rscheck() and rewrite() to distinguish the various 2328147078Sgshapiro ** calls and only trigger the error if necessary. For now just 2329147078Sgshapiro ** undo the change from 8.13.0. 2330147078Sgshapiro */ 2331147078Sgshapiro 2332132943Sgshapiro if (sz <= 0) 2333141858Sgshapiro usrerr("cataddr: string too long"); 2334147078Sgshapiro#endif 233538032Speter *p = '\0'; 2336168515Sgshapiro 2337168515Sgshapiro if (tTd(59, 14)) 2338168515Sgshapiro sm_dprintf(" cataddr => %s\n", str2prt(buf)); 233938032Speter} 2340168515Sgshapiro 234190792Sgshapiro/* 234238032Speter** SAMEADDR -- Determine if two addresses are the same 234338032Speter** 234438032Speter** This is not just a straight comparison -- if the mailer doesn't 234538032Speter** care about the host we just ignore it, etc. 234638032Speter** 234738032Speter** Parameters: 234838032Speter** a, b -- pointers to the internal forms to compare. 234938032Speter** 235038032Speter** Returns: 235190792Sgshapiro** true -- they represent the same mailbox. 235290792Sgshapiro** false -- they don't. 235338032Speter** 235438032Speter** Side Effects: 235538032Speter** none. 235638032Speter*/ 235738032Speter 235838032Speterbool 235938032Spetersameaddr(a, b) 236038032Speter register ADDRESS *a; 236138032Speter register ADDRESS *b; 236238032Speter{ 236338032Speter register ADDRESS *ca, *cb; 236438032Speter 236538032Speter /* if they don't have the same mailer, forget it */ 236638032Speter if (a->q_mailer != b->q_mailer) 236790792Sgshapiro return false; 236838032Speter 2369249729Sgshapiro /* 2370249729Sgshapiro ** Addresses resolving to error mailer 2371249729Sgshapiro ** should not be considered identical 2372249729Sgshapiro */ 2373249729Sgshapiro 2374249729Sgshapiro if (a->q_mailer == &errormailer) 2375249729Sgshapiro return false; 2376249729Sgshapiro 237738032Speter /* if the user isn't the same, we can drop out */ 237838032Speter if (strcmp(a->q_user, b->q_user) != 0) 237990792Sgshapiro return false; 238038032Speter 2381285229Sgshapiro /* do the required flags match? */ 2382285229Sgshapiro if (!ADDR_FLAGS_MATCH(a, b)) 2383285229Sgshapiro return false; 2384285229Sgshapiro 238538032Speter /* if we have good uids for both but they differ, these are different */ 238638032Speter if (a->q_mailer == ProgMailer) 238738032Speter { 238838032Speter ca = getctladdr(a); 238938032Speter cb = getctladdr(b); 239038032Speter if (ca != NULL && cb != NULL && 239138032Speter bitset(QGOODUID, ca->q_flags & cb->q_flags) && 239238032Speter ca->q_uid != cb->q_uid) 239390792Sgshapiro return false; 239438032Speter } 239538032Speter 239638032Speter /* otherwise compare hosts (but be careful for NULL ptrs) */ 239738032Speter if (a->q_host == b->q_host) 239838032Speter { 239938032Speter /* probably both null pointers */ 240090792Sgshapiro return true; 240138032Speter } 240238032Speter if (a->q_host == NULL || b->q_host == NULL) 240338032Speter { 240438032Speter /* only one is a null pointer */ 240590792Sgshapiro return false; 240638032Speter } 240738032Speter if (strcmp(a->q_host, b->q_host) != 0) 240890792Sgshapiro return false; 240938032Speter 241090792Sgshapiro return true; 241138032Speter} 241290792Sgshapiro/* 241338032Speter** PRINTADDR -- print address (for debugging) 241438032Speter** 241538032Speter** Parameters: 241638032Speter** a -- the address to print 241738032Speter** follow -- follow the q_next chain. 241838032Speter** 241938032Speter** Returns: 242038032Speter** none. 242138032Speter** 242238032Speter** Side Effects: 242338032Speter** none. 242438032Speter*/ 242538032Speter 242638032Speterstruct qflags 242738032Speter{ 242890792Sgshapiro char *qf_name; 242990792Sgshapiro unsigned long qf_bit; 243038032Speter}; 243138032Speter 2432285229Sgshapiro/* :'a,.s;^#define \(Q[A-Z]*\) .*; { "\1", \1 },; */ 243364562Sgshapirostatic struct qflags AddressFlags[] = 243438032Speter{ 243538032Speter { "QGOODUID", QGOODUID }, 243638032Speter { "QPRIMARY", QPRIMARY }, 243738032Speter { "QNOTREMOTE", QNOTREMOTE }, 243838032Speter { "QSELFREF", QSELFREF }, 243938032Speter { "QBOGUSSHELL", QBOGUSSHELL }, 244038032Speter { "QUNSAFEADDR", QUNSAFEADDR }, 244138032Speter { "QPINGONSUCCESS", QPINGONSUCCESS }, 244238032Speter { "QPINGONFAILURE", QPINGONFAILURE }, 244338032Speter { "QPINGONDELAY", QPINGONDELAY }, 244438032Speter { "QHASNOTIFY", QHASNOTIFY }, 244538032Speter { "QRELAYED", QRELAYED }, 244638032Speter { "QEXPANDED", QEXPANDED }, 244738032Speter { "QDELIVERED", QDELIVERED }, 244838032Speter { "QDELAYED", QDELAYED }, 244938032Speter { "QTHISPASS", QTHISPASS }, 2450285229Sgshapiro { "QALIAS", QALIAS }, 2451285229Sgshapiro { "QBYTRACE", QBYTRACE }, 2452285229Sgshapiro { "QBYNDELAY", QBYNDELAY }, 2453285229Sgshapiro { "QBYNRELAY", QBYNRELAY }, 2454285229Sgshapiro { "QINTBCC", QINTBCC }, 2455285229Sgshapiro { "QDYNMAILER", QDYNMAILER }, 245638032Speter { "QRCPTOK", QRCPTOK }, 2457363466Sgshapiro { "QSECURE", QSECURE }, 2458363466Sgshapiro { "QTHISPASS", QTHISPASS }, 2459363466Sgshapiro { "QRCPTOK", QRCPTOK }, 2460363466Sgshapiro { "QQUEUED", QQUEUED }, 246171345Sgshapiro { NULL, 0 } 246238032Speter}; 246338032Speter 246438032Spetervoid 2465132943Sgshapiroprintaddr(fp, a, follow) 2466132943Sgshapiro SM_FILE_T *fp; 246738032Speter register ADDRESS *a; 246838032Speter bool follow; 246938032Speter{ 247038032Speter register MAILER *m; 247138032Speter MAILER pseudomailer; 247238032Speter register struct qflags *qfp; 247338032Speter bool firstone; 247438032Speter 247538032Speter if (a == NULL) 247638032Speter { 2477132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n"); 247838032Speter return; 247938032Speter } 248038032Speter 248138032Speter while (a != NULL) 248238032Speter { 2483363466Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", (void *)a); 2484132943Sgshapiro (void) sm_io_flush(fp, SM_TIME_DEFAULT); 248538032Speter 248638032Speter /* find the mailer -- carefully */ 248738032Speter m = a->q_mailer; 248838032Speter if (m == NULL) 248938032Speter { 249038032Speter m = &pseudomailer; 249138032Speter m->m_mno = -1; 249238032Speter m->m_name = "NULL"; 249338032Speter } 249438032Speter 2495132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 249690792Sgshapiro "%s:\n\tmailer %d (%s), host `%s'\n", 249790792Sgshapiro a->q_paddr == NULL ? "<null>" : a->q_paddr, 249890792Sgshapiro m->m_mno, m->m_name, 249990792Sgshapiro a->q_host == NULL ? "<null>" : a->q_host); 2500132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 250190792Sgshapiro "\tuser `%s', ruser `%s'\n", 250290792Sgshapiro a->q_user, 250390792Sgshapiro a->q_ruser == NULL ? "<null>" : a->q_ruser); 2504132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate="); 250564562Sgshapiro switch (a->q_state) 250664562Sgshapiro { 250764562Sgshapiro case QS_OK: 2508132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK"); 250964562Sgshapiro break; 251064562Sgshapiro 251164562Sgshapiro case QS_DONTSEND: 2512132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 251390792Sgshapiro "DONTSEND"); 251464562Sgshapiro break; 251564562Sgshapiro 251664562Sgshapiro case QS_BADADDR: 2517132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 251890792Sgshapiro "BADADDR"); 251964562Sgshapiro break; 252064562Sgshapiro 252164562Sgshapiro case QS_QUEUEUP: 2522132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 252390792Sgshapiro "QUEUEUP"); 252464562Sgshapiro break; 252564562Sgshapiro 252690792Sgshapiro case QS_RETRY: 2527132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY"); 252890792Sgshapiro break; 252990792Sgshapiro 253064562Sgshapiro case QS_SENT: 2531132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT"); 253264562Sgshapiro break; 253364562Sgshapiro 253464562Sgshapiro case QS_VERIFIED: 2535132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 253690792Sgshapiro "VERIFIED"); 253764562Sgshapiro break; 253864562Sgshapiro 253964562Sgshapiro case QS_EXPANDED: 2540132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 254190792Sgshapiro "EXPANDED"); 254264562Sgshapiro break; 254364562Sgshapiro 254464562Sgshapiro case QS_SENDER: 2545132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 254690792Sgshapiro "SENDER"); 254764562Sgshapiro break; 254864562Sgshapiro 254964562Sgshapiro case QS_CLONED: 2550132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 255190792Sgshapiro "CLONED"); 255264562Sgshapiro break; 255364562Sgshapiro 255464562Sgshapiro case QS_DISCARDED: 2555132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 255690792Sgshapiro "DISCARDED"); 255764562Sgshapiro break; 255864562Sgshapiro 255964562Sgshapiro case QS_REPLACED: 2560132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 256190792Sgshapiro "REPLACED"); 256264562Sgshapiro break; 256364562Sgshapiro 256464562Sgshapiro case QS_REMOVED: 2565132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 256690792Sgshapiro "REMOVED"); 256764562Sgshapiro break; 256864562Sgshapiro 256964562Sgshapiro case QS_DUPLICATE: 2570132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 257190792Sgshapiro "DUPLICATE"); 257264562Sgshapiro break; 257364562Sgshapiro 257464562Sgshapiro case QS_INCLUDED: 2575132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 257690792Sgshapiro "INCLUDED"); 257764562Sgshapiro break; 257864562Sgshapiro 257964562Sgshapiro default: 2580132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 258190792Sgshapiro "%d", a->q_state); 258264562Sgshapiro break; 258364562Sgshapiro } 2584132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 258590792Sgshapiro ", next=%p, alias %p, uid %d, gid %d\n", 2586363466Sgshapiro (void *)a->q_next, (void *)a->q_alias, 258790792Sgshapiro (int) a->q_uid, (int) a->q_gid); 2588132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<", 258990792Sgshapiro a->q_flags); 259090792Sgshapiro firstone = true; 259138032Speter for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) 259238032Speter { 259338032Speter if (!bitset(qfp->qf_bit, a->q_flags)) 259438032Speter continue; 259538032Speter if (!firstone) 2596132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 259790792Sgshapiro ","); 259890792Sgshapiro firstone = false; 2599132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 260090792Sgshapiro qfp->qf_name); 260138032Speter } 2602132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n"); 2603132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 260490792Sgshapiro "\towner=%s, home=\"%s\", fullname=\"%s\"\n", 260590792Sgshapiro a->q_owner == NULL ? "(none)" : a->q_owner, 260690792Sgshapiro a->q_home == NULL ? "(none)" : a->q_home, 260790792Sgshapiro a->q_fullname == NULL ? "(none)" : a->q_fullname); 2608132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 260990792Sgshapiro "\torcpt=\"%s\", statmta=%s, status=%s\n", 261090792Sgshapiro a->q_orcpt == NULL ? "(none)" : a->q_orcpt, 261190792Sgshapiro a->q_statmta == NULL ? "(none)" : a->q_statmta, 261290792Sgshapiro a->q_status == NULL ? "(none)" : a->q_status); 2613132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 261490792Sgshapiro "\tfinalrcpt=\"%s\"\n", 261590792Sgshapiro a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt); 2616132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 261790792Sgshapiro "\trstatus=\"%s\"\n", 261890792Sgshapiro a->q_rstatus == NULL ? "(none)" : a->q_rstatus); 2619132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 262090792Sgshapiro "\tstatdate=%s\n", 262190792Sgshapiro a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate)); 262238032Speter 262338032Speter if (!follow) 262438032Speter return; 262538032Speter a = a->q_next; 262638032Speter } 262738032Speter} 262890792Sgshapiro/* 262990792Sgshapiro** EMPTYADDR -- return true if this address is empty (``<>'') 263038032Speter** 263138032Speter** Parameters: 263238032Speter** a -- pointer to the address 263338032Speter** 263438032Speter** Returns: 263590792Sgshapiro** true -- if this address is "empty" (i.e., no one should 263638032Speter** ever generate replies to it. 263790792Sgshapiro** false -- if it is a "regular" (read: replyable) address. 263838032Speter*/ 263938032Speter 264038032Speterbool 264138032Speteremptyaddr(a) 264238032Speter register ADDRESS *a; 264338032Speter{ 264438032Speter return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 || 264538032Speter a->q_user == NULL || strcmp(a->q_user, "<>") == 0; 264638032Speter} 264790792Sgshapiro/* 264838032Speter** REMOTENAME -- return the name relative to the current mailer 264938032Speter** 265038032Speter** Parameters: 265138032Speter** name -- the name to translate. 2652120256Sgshapiro** m -- the mailer that we want to do rewriting relative to. 265338032Speter** flags -- fine tune operations. 265438032Speter** pstat -- pointer to status word. 265538032Speter** e -- the current envelope. 265638032Speter** 265738032Speter** Returns: 265838032Speter** the text string representing this address relative to 265938032Speter** the receiving mailer. 266038032Speter** 266138032Speter** Side Effects: 266238032Speter** none. 266338032Speter** 266438032Speter** Warnings: 266538032Speter** The text string returned is tucked away locally; 266638032Speter** copy it if you intend to save it. 266738032Speter*/ 266838032Speter 266938032Speterchar * 267038032Speterremotename(name, m, flags, pstat, e) 267138032Speter char *name; 267238032Speter struct mailer *m; 267338032Speter int flags; 267438032Speter int *pstat; 267538032Speter register ENVELOPE *e; 267638032Speter{ 267738032Speter register char **pvp; 267890792Sgshapiro char *SM_NONVOLATILE fancy; 267990792Sgshapiro char *oldg; 268038032Speter int rwset; 268138032Speter static char buf[MAXNAME + 1]; 268238032Speter char lbuf[MAXNAME + 1]; 268338032Speter char pvpbuf[PSBUFSIZE]; 268464562Sgshapiro char addrtype[4]; 268538032Speter 268638032Speter if (tTd(12, 1)) 2687168515Sgshapiro { 2688168515Sgshapiro sm_dprintf("remotename("); 2689168515Sgshapiro xputs(sm_debug_file(), name); 2690168515Sgshapiro sm_dprintf(")\n"); 2691168515Sgshapiro } 269238032Speter 269338032Speter /* don't do anything if we are tagging it as special */ 269438032Speter if (bitset(RF_SENDERADDR, flags)) 269564562Sgshapiro { 269638032Speter rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset 269738032Speter : m->m_se_rwset; 269864562Sgshapiro addrtype[2] = 's'; 269964562Sgshapiro } 270038032Speter else 270164562Sgshapiro { 270238032Speter rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset 270338032Speter : m->m_re_rwset; 270464562Sgshapiro addrtype[2] = 'r'; 270564562Sgshapiro } 270638032Speter if (rwset < 0) 270764562Sgshapiro return name; 270864562Sgshapiro addrtype[1] = ' '; 270964562Sgshapiro addrtype[3] = '\0'; 271064562Sgshapiro addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e'; 271190792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype); 271238032Speter 271338032Speter /* 271438032Speter ** Do a heuristic crack of this name to extract any comment info. 271538032Speter ** This will leave the name as a comment and a $g macro. 271638032Speter */ 271738032Speter 271838032Speter if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags)) 271938032Speter fancy = "\201g"; 272038032Speter else 2721111823Sgshapiro fancy = crackaddr(name, e); 272238032Speter 272338032Speter /* 272438032Speter ** Turn the name into canonical form. 272538032Speter ** Normally this will be RFC 822 style, i.e., "user@domain". 272638032Speter ** If this only resolves to "user", and the "C" flag is 272738032Speter ** specified in the sending mailer, then the sender's 272838032Speter ** domain will be appended. 272938032Speter */ 273038032Speter 2731168515Sgshapiro pvp = prescan(name, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, false); 273238032Speter if (pvp == NULL) 273364562Sgshapiro return name; 273490792Sgshapiro if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 273538032Speter *pstat = EX_TEMPFAIL; 273638032Speter if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) 273738032Speter { 273838032Speter /* append from domain to this address */ 273938032Speter register char **pxp = pvp; 274064562Sgshapiro int l = MAXATOM; /* size of buffer for pvp */ 274138032Speter 274238032Speter /* see if there is an "@domain" in the current name */ 274338032Speter while (*pxp != NULL && strcmp(*pxp, "@") != 0) 274464562Sgshapiro { 274538032Speter pxp++; 274664562Sgshapiro --l; 274764562Sgshapiro } 274838032Speter if (*pxp == NULL) 274938032Speter { 275038032Speter /* no.... append the "@domain" from the sender */ 275138032Speter register char **qxq = e->e_fromdomain; 275238032Speter 275338032Speter while ((*pxp++ = *qxq++) != NULL) 275464562Sgshapiro { 275564562Sgshapiro if (--l <= 0) 275664562Sgshapiro { 275764562Sgshapiro *--pxp = NULL; 275864562Sgshapiro usrerr("553 5.1.0 remotename: too many tokens"); 275964562Sgshapiro *pstat = EX_UNAVAILABLE; 276064562Sgshapiro break; 276164562Sgshapiro } 276264562Sgshapiro } 276390792Sgshapiro if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 276438032Speter *pstat = EX_TEMPFAIL; 276538032Speter } 276638032Speter } 276738032Speter 276838032Speter /* 276938032Speter ** Do more specific rewriting. 277038032Speter ** Rewrite using ruleset 1 or 2 depending on whether this is 277138032Speter ** a sender address or not. 277238032Speter ** Then run it through any receiving-mailer-specific rulesets. 277338032Speter */ 277438032Speter 277538032Speter if (bitset(RF_SENDERADDR, flags)) 277638032Speter { 277790792Sgshapiro if (REWRITE(pvp, 1, e) == EX_TEMPFAIL) 277838032Speter *pstat = EX_TEMPFAIL; 277938032Speter } 278038032Speter else 278138032Speter { 278290792Sgshapiro if (REWRITE(pvp, 2, e) == EX_TEMPFAIL) 278338032Speter *pstat = EX_TEMPFAIL; 278438032Speter } 278538032Speter if (rwset > 0) 278638032Speter { 278790792Sgshapiro if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL) 278838032Speter *pstat = EX_TEMPFAIL; 278938032Speter } 279038032Speter 279138032Speter /* 279238032Speter ** Do any final sanitation the address may require. 279338032Speter ** This will normally be used to turn internal forms 279438032Speter ** (e.g., user@host.LOCAL) into external form. This 279538032Speter ** may be used as a default to the above rules. 279638032Speter */ 279738032Speter 279890792Sgshapiro if (REWRITE(pvp, 4, e) == EX_TEMPFAIL) 279938032Speter *pstat = EX_TEMPFAIL; 280038032Speter 280138032Speter /* 280238032Speter ** Now restore the comment information we had at the beginning. 280338032Speter */ 280438032Speter 2805168515Sgshapiro cataddr(pvp, NULL, lbuf, sizeof(lbuf), '\0', false); 280690792Sgshapiro oldg = macget(&e->e_macro, 'g'); 280790792Sgshapiro macset(&e->e_macro, 'g', lbuf); 280838032Speter 280990792Sgshapiro SM_TRY 281090792Sgshapiro /* need to make sure route-addrs have <angle brackets> */ 281190792Sgshapiro if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') 2812168515Sgshapiro expand("<\201g>", buf, sizeof(buf), e); 281390792Sgshapiro else 2814168515Sgshapiro expand(fancy, buf, sizeof(buf), e); 281590792Sgshapiro SM_FINALLY 281690792Sgshapiro macset(&e->e_macro, 'g', oldg); 281790792Sgshapiro SM_END_TRY 281838032Speter 281938032Speter if (tTd(12, 1)) 2820168515Sgshapiro { 2821168515Sgshapiro sm_dprintf("remotename => `"); 2822168515Sgshapiro xputs(sm_debug_file(), buf); 2823285229Sgshapiro sm_dprintf("', stat=%d\n", *pstat); 2824168515Sgshapiro } 282564562Sgshapiro return buf; 282638032Speter} 282790792Sgshapiro/* 282838032Speter** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 282938032Speter** 283038032Speter** Parameters: 283138032Speter** a -- the address to map (but just the user name part). 283238032Speter** sendq -- the sendq in which to install any replacement 283338032Speter** addresses. 283438032Speter** aliaslevel -- the alias nesting depth. 283538032Speter** e -- the envelope. 283638032Speter** 283738032Speter** Returns: 283838032Speter** none. 283938032Speter*/ 284038032Speter 284138032Speter#define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\ 284238032Speter Q_PINGFLAGS|QHASNOTIFY|\ 284390792Sgshapiro QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\ 284490792Sgshapiro QBYTRACE|QBYNDELAY|QBYNRELAY) 284538032Speter 284638032Spetervoid 284738032Spetermaplocaluser(a, sendq, aliaslevel, e) 284838032Speter register ADDRESS *a; 284938032Speter ADDRESS **sendq; 285038032Speter int aliaslevel; 285138032Speter ENVELOPE *e; 285238032Speter{ 285338032Speter register char **pvp; 285490792Sgshapiro register ADDRESS *SM_NONVOLATILE a1 = NULL; 285538032Speter char pvpbuf[PSBUFSIZE]; 285638032Speter 285738032Speter if (tTd(29, 1)) 285838032Speter { 285990792Sgshapiro sm_dprintf("maplocaluser: "); 2860132943Sgshapiro printaddr(sm_debug_file(), a, false); 286138032Speter } 2862168515Sgshapiro pvp = prescan(a->q_user, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, 2863168515Sgshapiro false); 286438032Speter if (pvp == NULL) 286538032Speter { 286638032Speter if (tTd(29, 9)) 286790792Sgshapiro sm_dprintf("maplocaluser: cannot prescan %s\n", 286864562Sgshapiro a->q_user); 286938032Speter return; 287038032Speter } 287138032Speter 287290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', a->q_host); 287390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', a->q_user); 287490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', a->q_home); 287564562Sgshapiro 287690792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); 287790792Sgshapiro if (REWRITE(pvp, 5, e) == EX_TEMPFAIL) 287838032Speter { 287938032Speter if (tTd(29, 9)) 288090792Sgshapiro sm_dprintf("maplocaluser: rewrite tempfail\n"); 288164562Sgshapiro a->q_state = QS_QUEUEUP; 288238032Speter a->q_status = "4.4.3"; 288338032Speter return; 288438032Speter } 288538032Speter if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 288638032Speter { 288738032Speter if (tTd(29, 9)) 288890792Sgshapiro sm_dprintf("maplocaluser: doesn't resolve\n"); 288938032Speter return; 289038032Speter } 289138032Speter 289290792Sgshapiro SM_TRY 289390792Sgshapiro a1 = buildaddr(pvp, NULL, 0, e); 289490792Sgshapiro SM_EXCEPT(exc, "E:mta.quickabort") 289590792Sgshapiro 289690792Sgshapiro /* 289790792Sgshapiro ** mark address as bad, S5 returned an error 289890792Sgshapiro ** and we gave that back to the SMTP client. 289990792Sgshapiro */ 290090792Sgshapiro 290190792Sgshapiro a->q_state = QS_DONTSEND; 290290792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 2); 290390792Sgshapiro SM_END_TRY 290490792Sgshapiro 290538032Speter /* if non-null, mailer destination specified -- has it changed? */ 290638032Speter if (a1 == NULL || sameaddr(a, a1)) 290738032Speter { 290838032Speter if (tTd(29, 9)) 290990792Sgshapiro sm_dprintf("maplocaluser: address unchanged\n"); 291038032Speter return; 291138032Speter } 291238032Speter 291338032Speter /* make new address take on flags and print attributes of old */ 291438032Speter a1->q_flags &= ~Q_COPYFLAGS; 291538032Speter a1->q_flags |= a->q_flags & Q_COPYFLAGS; 291690792Sgshapiro a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr); 291790792Sgshapiro a1->q_finalrcpt = a->q_finalrcpt; 291864562Sgshapiro a1->q_orcpt = a->q_orcpt; 291938032Speter 292038032Speter /* mark old address as dead; insert new address */ 292164562Sgshapiro a->q_state = QS_REPLACED; 292238032Speter if (tTd(29, 5)) 292338032Speter { 292490792Sgshapiro sm_dprintf("maplocaluser: QS_REPLACED "); 2925132943Sgshapiro printaddr(sm_debug_file(), a, false); 292638032Speter } 292738032Speter a1->q_alias = a; 292890792Sgshapiro allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e); 292938032Speter (void) recipient(a1, sendq, aliaslevel, e); 293038032Speter} 293190792Sgshapiro/* 293238032Speter** DEQUOTE_INIT -- initialize dequote map 293338032Speter** 293438032Speter** Parameters: 293538032Speter** map -- the internal map structure. 293638032Speter** args -- arguments. 293738032Speter** 293838032Speter** Returns: 293990792Sgshapiro** true. 294038032Speter*/ 294138032Speter 294238032Speterbool 294338032Speterdequote_init(map, args) 294438032Speter MAP *map; 294538032Speter char *args; 294638032Speter{ 294738032Speter register char *p = args; 294838032Speter 294964562Sgshapiro /* there is no check whether there is really an argument */ 295038032Speter map->map_mflags |= MF_KEEPQUOTES; 295138032Speter for (;;) 295238032Speter { 2953363466Sgshapiro while (SM_ISSPACE(*p)) 295438032Speter p++; 295538032Speter if (*p != '-') 295638032Speter break; 295738032Speter switch (*++p) 295838032Speter { 295938032Speter case 'a': 296038032Speter map->map_app = ++p; 296138032Speter break; 296238032Speter 296364562Sgshapiro case 'D': 296464562Sgshapiro map->map_mflags |= MF_DEFER; 296564562Sgshapiro break; 296664562Sgshapiro 296764562Sgshapiro case 'S': 296838032Speter case 's': 296964562Sgshapiro map->map_spacesub = *++p; 297038032Speter break; 297138032Speter } 2972363466Sgshapiro while (*p != '\0' && !(SM_ISSPACE(*p))) 297338032Speter p++; 297438032Speter if (*p != '\0') 297538032Speter *p = '\0'; 297638032Speter } 297738032Speter if (map->map_app != NULL) 297838032Speter map->map_app = newstr(map->map_app); 297938032Speter 298090792Sgshapiro return true; 298138032Speter} 298290792Sgshapiro/* 298338032Speter** DEQUOTE_MAP -- unquote an address 298438032Speter** 298538032Speter** Parameters: 298638032Speter** map -- the internal map structure (ignored). 298738032Speter** name -- the name to dequote. 298838032Speter** av -- arguments (ignored). 298938032Speter** statp -- pointer to status out-parameter. 299038032Speter** 299138032Speter** Returns: 299238032Speter** NULL -- if there were no quotes, or if the resulting 299338032Speter** unquoted buffer would not be acceptable to prescan. 299438032Speter** else -- The dequoted buffer. 299538032Speter*/ 299638032Speter 299738032Speter/* ARGSUSED2 */ 299838032Speterchar * 299938032Speterdequote_map(map, name, av, statp) 300038032Speter MAP *map; 300138032Speter char *name; 300238032Speter char **av; 300338032Speter int *statp; 300438032Speter{ 300538032Speter register char *p; 300638032Speter register char *q; 300738032Speter register char c; 300838032Speter int anglecnt = 0; 300938032Speter int cmntcnt = 0; 301038032Speter int quotecnt = 0; 301138032Speter int spacecnt = 0; 301290792Sgshapiro bool quotemode = false; 301390792Sgshapiro bool bslashmode = false; 301464562Sgshapiro char spacesub = map->map_spacesub; 301538032Speter 301638032Speter for (p = q = name; (c = *p++) != '\0'; ) 301738032Speter { 301838032Speter if (bslashmode) 301938032Speter { 302090792Sgshapiro bslashmode = false; 302138032Speter *q++ = c; 302238032Speter continue; 302338032Speter } 302438032Speter 302538032Speter if (c == ' ' && spacesub != '\0') 302638032Speter c = spacesub; 302738032Speter 302838032Speter switch (c) 302938032Speter { 303038032Speter case '\\': 303190792Sgshapiro bslashmode = true; 303238032Speter break; 303338032Speter 303438032Speter case '(': 303538032Speter cmntcnt++; 303638032Speter break; 303738032Speter 303838032Speter case ')': 303938032Speter if (cmntcnt-- <= 0) 304038032Speter return NULL; 304138032Speter break; 304238032Speter 304338032Speter case ' ': 304464562Sgshapiro case '\t': 304538032Speter spacecnt++; 304638032Speter break; 304738032Speter } 304838032Speter 304938032Speter if (cmntcnt > 0) 305038032Speter { 305138032Speter *q++ = c; 305238032Speter continue; 305338032Speter } 305438032Speter 305538032Speter switch (c) 305638032Speter { 305738032Speter case '"': 305838032Speter quotemode = !quotemode; 305938032Speter quotecnt++; 306038032Speter continue; 306138032Speter 306238032Speter case '<': 306338032Speter anglecnt++; 306438032Speter break; 306538032Speter 306638032Speter case '>': 306738032Speter if (anglecnt-- <= 0) 306838032Speter return NULL; 306938032Speter break; 307038032Speter } 307138032Speter *q++ = c; 307238032Speter } 307338032Speter 307438032Speter if (anglecnt != 0 || cmntcnt != 0 || bslashmode || 307538032Speter quotemode || quotecnt <= 0 || spacecnt != 0) 307638032Speter return NULL; 307738032Speter *q++ = '\0'; 307838032Speter return map_rewrite(map, name, strlen(name), NULL); 307938032Speter} 308090792Sgshapiro/* 308138032Speter** RSCHECK -- check string(s) for validity using rewriting sets 308238032Speter** 308338032Speter** Parameters: 308438032Speter** rwset -- the rewriting set to use. 308538032Speter** p1 -- the first string to check. 3086363466Sgshapiro** p2 -- the second string to check -- may be NULL. 308738032Speter** e -- the current envelope. 3088102528Sgshapiro** flags -- control some behavior, see RSF_ in sendmail.h 308990792Sgshapiro** logl -- logging level. 309071345Sgshapiro** host -- NULL or relay host. 309190792Sgshapiro** logid -- id for sm_syslog. 3092168515Sgshapiro** addr -- if not NULL and ruleset returns $#error: 3093168515Sgshapiro** store mailer triple here. 3094285229Sgshapiro** addrstr -- if not NULL and ruleset does not return $#: 3095285229Sgshapiro** address string 309638032Speter** 309738032Speter** Returns: 309838032Speter** EX_OK -- if the rwset doesn't resolve to $#error 309938032Speter** else -- the failure status (message printed) 310038032Speter*/ 310138032Speter 310238032Speterint 3103285229Sgshapirorscheck(rwset, p1, p2, e, flags, logl, host, logid, addr, addrstr) 310438032Speter char *rwset; 3105363466Sgshapiro const char *p1; 3106363466Sgshapiro const char *p2; 310738032Speter ENVELOPE *e; 3108102528Sgshapiro int flags; 310964562Sgshapiro int logl; 3110363466Sgshapiro const char *host; 3111363466Sgshapiro const char *logid; 3112168515Sgshapiro ADDRESS *addr; 3113285229Sgshapiro char **addrstr; 311438032Speter{ 311590792Sgshapiro char *volatile buf; 3116157001Sgshapiro size_t bufsize; 311738032Speter int saveexitstat; 311890792Sgshapiro int volatile rstat = EX_OK; 311938032Speter char **pvp; 312038032Speter int rsno; 312190792Sgshapiro bool volatile discard = false; 312238032Speter bool saveQuickAbort = QuickAbort; 312338032Speter bool saveSuprErrs = SuprErrs; 312490792Sgshapiro bool quarantine = false; 312590792Sgshapiro char ubuf[BUFSIZ * 2]; 312638032Speter char buf0[MAXLINE]; 312738032Speter char pvpbuf[PSBUFSIZE]; 312838032Speter extern char MsgBuf[]; 312938032Speter 313038032Speter if (tTd(48, 2)) 313190792Sgshapiro sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1, 313238032Speter p2 == NULL ? "(NULL)" : p2); 313338032Speter 313438032Speter rsno = strtorwset(rwset, NULL, ST_FIND); 313538032Speter if (rsno < 0) 313638032Speter return EX_OK; 313738032Speter 313838032Speter if (p2 != NULL) 313938032Speter { 314038032Speter bufsize = strlen(p1) + strlen(p2) + 2; 3141168515Sgshapiro if (bufsize > sizeof(buf0)) 314290792Sgshapiro buf = sm_malloc_x(bufsize); 314338032Speter else 314438032Speter { 314538032Speter buf = buf0; 3146168515Sgshapiro bufsize = sizeof(buf0); 314738032Speter } 314890792Sgshapiro (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); 314938032Speter } 315038032Speter else 315138032Speter { 315238032Speter bufsize = strlen(p1) + 1; 3153168515Sgshapiro if (bufsize > sizeof(buf0)) 315490792Sgshapiro buf = sm_malloc_x(bufsize); 315538032Speter else 315638032Speter { 315738032Speter buf = buf0; 3158168515Sgshapiro bufsize = sizeof(buf0); 315938032Speter } 316090792Sgshapiro (void) sm_strlcpy(buf, p1, bufsize); 316138032Speter } 316290792Sgshapiro SM_TRY 316338032Speter { 316490792Sgshapiro SuprErrs = true; 316590792Sgshapiro QuickAbort = false; 3166168515Sgshapiro pvp = prescan(buf, '\0', pvpbuf, sizeof(pvpbuf), NULL, 3167168515Sgshapiro bitset(RSF_RMCOMM, flags) ? 3168168515Sgshapiro IntTokenTab : TokTypeNoC, 3169132943Sgshapiro bitset(RSF_RMCOMM, flags) ? false : true); 317090792Sgshapiro SuprErrs = saveSuprErrs; 317190792Sgshapiro if (pvp == NULL) 317290792Sgshapiro { 317390792Sgshapiro if (tTd(48, 2)) 317490792Sgshapiro sm_dprintf("rscheck: cannot prescan input\n"); 317590792Sgshapiro /* 317690792Sgshapiro syserr("rscheck: cannot prescan input: \"%s\"", 317790792Sgshapiro shortenstring(buf, MAXSHORTSTR)); 317890792Sgshapiro rstat = EX_DATAERR; 317990792Sgshapiro */ 318090792Sgshapiro goto finis; 318190792Sgshapiro } 3182102528Sgshapiro if (bitset(RSF_UNSTRUCTURED, flags)) 3183102528Sgshapiro SuprErrs = true; 318490792Sgshapiro (void) REWRITE(pvp, rsno, e); 3185102528Sgshapiro if (bitset(RSF_UNSTRUCTURED, flags)) 3186102528Sgshapiro SuprErrs = saveSuprErrs; 3187285229Sgshapiro 3188285229Sgshapiro if (pvp[0] != NULL && (pvp[0][0] & 0377) != CANONNET && 3189285229Sgshapiro bitset(RSF_ADDR, flags) && addrstr != NULL) 3190285229Sgshapiro { 3191285229Sgshapiro cataddr(&(pvp[0]), NULL, ubuf, sizeof(ubuf), 3192285229Sgshapiro bitset(RSF_STRING, flags) ? NOSPACESEP : ' ', 3193285229Sgshapiro true); 3194285229Sgshapiro *addrstr = sm_rpool_strdup_x(e->e_rpool, ubuf); 3195285229Sgshapiro goto finis; 3196285229Sgshapiro } 3197285229Sgshapiro 319890792Sgshapiro if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || 319990792Sgshapiro pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 && 320090792Sgshapiro strcmp(pvp[1], "discard") != 0)) 320190792Sgshapiro { 320290792Sgshapiro goto finis; 320390792Sgshapiro } 320466494Sgshapiro 320590792Sgshapiro if (strcmp(pvp[1], "discard") == 0) 320690792Sgshapiro { 320790792Sgshapiro if (tTd(48, 2)) 320890792Sgshapiro sm_dprintf("rscheck: discard mailer selected\n"); 320990792Sgshapiro e->e_flags |= EF_DISCARD; 321090792Sgshapiro discard = true; 321190792Sgshapiro } 321290792Sgshapiro else if (strcmp(pvp[1], "error") == 0 && 321390792Sgshapiro pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST && 321490792Sgshapiro pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0) 321590792Sgshapiro { 321690792Sgshapiro if (pvp[4] == NULL || 321790792Sgshapiro (pvp[4][0] & 0377) != CANONUSER || 321890792Sgshapiro pvp[5] == NULL) 321990792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 322090792Sgshapiro rwset); 322190792Sgshapiro else 322290792Sgshapiro { 322390792Sgshapiro cataddr(&(pvp[5]), NULL, ubuf, 3224168515Sgshapiro sizeof(ubuf), ' ', true); 322590792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 322690792Sgshapiro ubuf); 322790792Sgshapiro } 322890792Sgshapiro macdefine(&e->e_macro, A_PERM, 322990792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 323090792Sgshapiro quarantine = true; 323190792Sgshapiro } 323290792Sgshapiro else 323390792Sgshapiro { 3234168515Sgshapiro auto ADDRESS a1; 323590792Sgshapiro int savelogusrerrs = LogUsrErrs; 323690792Sgshapiro static bool logged = false; 323790792Sgshapiro 323890792Sgshapiro /* got an error -- process it */ 323990792Sgshapiro saveexitstat = ExitStat; 324090792Sgshapiro LogUsrErrs = false; 324190792Sgshapiro (void) buildaddr(pvp, &a1, 0, e); 3242168515Sgshapiro if (addr != NULL) 3243168515Sgshapiro { 3244168515Sgshapiro addr->q_mailer = a1.q_mailer; 3245168515Sgshapiro addr->q_user = a1.q_user; 3246168515Sgshapiro addr->q_host = a1.q_host; 3247168515Sgshapiro } 324890792Sgshapiro LogUsrErrs = savelogusrerrs; 324990792Sgshapiro rstat = ExitStat; 325090792Sgshapiro ExitStat = saveexitstat; 325190792Sgshapiro if (!logged) 325290792Sgshapiro { 3253102528Sgshapiro if (bitset(RSF_COUNT, flags)) 325490792Sgshapiro markstats(e, &a1, STATS_REJECT); 325590792Sgshapiro logged = true; 325690792Sgshapiro } 325790792Sgshapiro } 325890792Sgshapiro 325990792Sgshapiro if (LogLevel > logl) 326090792Sgshapiro { 3261363466Sgshapiro const char *relay; 326290792Sgshapiro char *p; 326390792Sgshapiro char lbuf[MAXLINE]; 326490792Sgshapiro 326590792Sgshapiro p = lbuf; 326690792Sgshapiro if (p2 != NULL) 326790792Sgshapiro { 326890792Sgshapiro (void) sm_snprintf(p, SPACELEFT(lbuf, p), 326990792Sgshapiro ", arg2=%s", 327090792Sgshapiro p2); 327190792Sgshapiro p += strlen(p); 327290792Sgshapiro } 327390792Sgshapiro 327490792Sgshapiro if (host != NULL) 327590792Sgshapiro relay = host; 327690792Sgshapiro else 327790792Sgshapiro relay = macvalue('_', e); 327890792Sgshapiro if (relay != NULL) 327990792Sgshapiro { 328090792Sgshapiro (void) sm_snprintf(p, SPACELEFT(lbuf, p), 328190792Sgshapiro ", relay=%s", relay); 328290792Sgshapiro p += strlen(p); 328390792Sgshapiro } 328490792Sgshapiro *p = '\0'; 328590792Sgshapiro if (discard) 328690792Sgshapiro sm_syslog(LOG_NOTICE, logid, 328790792Sgshapiro "ruleset=%s, arg1=%s%s, discard", 328890792Sgshapiro rwset, p1, lbuf); 328990792Sgshapiro else if (quarantine) 329090792Sgshapiro sm_syslog(LOG_NOTICE, logid, 329190792Sgshapiro "ruleset=%s, arg1=%s%s, quarantine=%s", 329290792Sgshapiro rwset, p1, lbuf, ubuf); 329390792Sgshapiro else 329490792Sgshapiro sm_syslog(LOG_NOTICE, logid, 329590792Sgshapiro "ruleset=%s, arg1=%s%s, reject=%s", 329690792Sgshapiro rwset, p1, lbuf, MsgBuf); 329790792Sgshapiro } 329890792Sgshapiro 329990792Sgshapiro finis: ; 330073188Sgshapiro } 330190792Sgshapiro SM_FINALLY 330238032Speter { 330390792Sgshapiro /* clean up */ 330490792Sgshapiro if (buf != buf0) 330590792Sgshapiro sm_free(buf); 330690792Sgshapiro QuickAbort = saveQuickAbort; 330738032Speter } 330890792Sgshapiro SM_END_TRY 330938032Speter 331090792Sgshapiro setstat(rstat); 331190792Sgshapiro 331290792Sgshapiro /* rulesets don't set errno */ 331390792Sgshapiro errno = 0; 331490792Sgshapiro if (rstat != EX_OK && QuickAbort) 331590792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 2); 331690792Sgshapiro return rstat; 331790792Sgshapiro} 3318363466Sgshapiro 331990792Sgshapiro/* 332090792Sgshapiro** RSCAP -- call rewriting set to return capabilities 332190792Sgshapiro** 332290792Sgshapiro** Parameters: 332390792Sgshapiro** rwset -- the rewriting set to use. 332490792Sgshapiro** p1 -- the first string to check. 3325363466Sgshapiro** p2 -- the second string to check -- may be NULL. 332690792Sgshapiro** e -- the current envelope. 332790792Sgshapiro** pvp -- pointer to token vector. 332890792Sgshapiro** pvpbuf -- buffer space. 3329120256Sgshapiro** size -- size of buffer space. 333090792Sgshapiro** 333190792Sgshapiro** Returns: 333290792Sgshapiro** EX_UNAVAILABLE -- ruleset doesn't exist. 333390792Sgshapiro** EX_DATAERR -- prescan() failed. 333490792Sgshapiro** EX_OK -- rewrite() was successful. 333590792Sgshapiro** else -- return status from rewrite(). 333690792Sgshapiro*/ 333790792Sgshapiro 333890792Sgshapiroint 333990792Sgshapirorscap(rwset, p1, p2, e, pvp, pvpbuf, size) 334090792Sgshapiro char *rwset; 334190792Sgshapiro char *p1; 334290792Sgshapiro char *p2; 334390792Sgshapiro ENVELOPE *e; 334490792Sgshapiro char ***pvp; 334590792Sgshapiro char *pvpbuf; 334690792Sgshapiro int size; 334790792Sgshapiro{ 334890792Sgshapiro char *volatile buf; 3349157001Sgshapiro size_t bufsize; 335090792Sgshapiro int volatile rstat = EX_OK; 335190792Sgshapiro int rsno; 335290792Sgshapiro bool saveQuickAbort = QuickAbort; 335390792Sgshapiro bool saveSuprErrs = SuprErrs; 335490792Sgshapiro char buf0[MAXLINE]; 335590792Sgshapiro extern char MsgBuf[]; 335690792Sgshapiro 335790792Sgshapiro if (tTd(48, 2)) 335890792Sgshapiro sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1, 335990792Sgshapiro p2 == NULL ? "(NULL)" : p2); 336090792Sgshapiro 3361159609Sgshapiro SM_REQUIRE(pvp != NULL); 336290792Sgshapiro rsno = strtorwset(rwset, NULL, ST_FIND); 336390792Sgshapiro if (rsno < 0) 336490792Sgshapiro return EX_UNAVAILABLE; 336590792Sgshapiro 336690792Sgshapiro if (p2 != NULL) 336738032Speter { 336890792Sgshapiro bufsize = strlen(p1) + strlen(p2) + 2; 3369168515Sgshapiro if (bufsize > sizeof(buf0)) 337090792Sgshapiro buf = sm_malloc_x(bufsize); 337190792Sgshapiro else 337290792Sgshapiro { 337390792Sgshapiro buf = buf0; 3374168515Sgshapiro bufsize = sizeof(buf0); 337590792Sgshapiro } 337690792Sgshapiro (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); 337738032Speter } 337864562Sgshapiro else 337938032Speter { 338090792Sgshapiro bufsize = strlen(p1) + 1; 3381168515Sgshapiro if (bufsize > sizeof(buf0)) 338290792Sgshapiro buf = sm_malloc_x(bufsize); 338390792Sgshapiro else 338438032Speter { 338590792Sgshapiro buf = buf0; 3386168515Sgshapiro bufsize = sizeof(buf0); 338738032Speter } 338890792Sgshapiro (void) sm_strlcpy(buf, p1, bufsize); 338938032Speter } 339090792Sgshapiro SM_TRY 339138032Speter { 339290792Sgshapiro SuprErrs = true; 339390792Sgshapiro QuickAbort = false; 3394168515Sgshapiro *pvp = prescan(buf, '\0', pvpbuf, size, NULL, IntTokenTab, 3395168515Sgshapiro false); 339690792Sgshapiro if (*pvp != NULL) 3397120256Sgshapiro rstat = rewrite(*pvp, rsno, 0, e, size); 339871345Sgshapiro else 339938032Speter { 340090792Sgshapiro if (tTd(48, 2)) 340190792Sgshapiro sm_dprintf("rscap: cannot prescan input\n"); 340290792Sgshapiro rstat = EX_DATAERR; 340338032Speter } 340438032Speter } 340590792Sgshapiro SM_FINALLY 340690792Sgshapiro { 340790792Sgshapiro /* clean up */ 340890792Sgshapiro if (buf != buf0) 340990792Sgshapiro sm_free(buf); 341090792Sgshapiro SuprErrs = saveSuprErrs; 341190792Sgshapiro QuickAbort = saveQuickAbort; 341238032Speter 341390792Sgshapiro /* prevent information leak, this may contain rewrite error */ 341490792Sgshapiro MsgBuf[0] = '\0'; 341590792Sgshapiro } 341690792Sgshapiro SM_END_TRY 341738032Speter return rstat; 341838032Speter} 3419