138032Speter/* 2168515Sgshapiro * Copyright (c) 1998-2006 Sendmail, 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 16249729SgshapiroSM_RCSID("@(#)$Id: parseaddr.c,v 8.406 2013/04/17 16:53:01 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; 13790792Sgshapiro if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 13890792Sgshapiro qup = true; 13990792Sgshapiro if (REWRITE(pvp, 0, e) == EX_TEMPFAIL) 14090792Sgshapiro qup = true; 14138032Speter 14238032Speter /* 14338032Speter ** Build canonical address from pvp. 14438032Speter */ 14538032Speter 14638032Speter a = buildaddr(pvp, a, flags, e); 14738032Speter 14894334Sgshapiro if (hasctrlchar(a->q_user, isrcpt, true)) 14990792Sgshapiro { 15090792Sgshapiro if (tTd(20, 1)) 15190792Sgshapiro sm_dprintf("parseaddr-->bad q_user\n"); 15294334Sgshapiro 15394334Sgshapiro /* 15494334Sgshapiro ** Just mark the address as bad so DSNs work. 15594334Sgshapiro ** hasctrlchar() has to make sure that the address 15694334Sgshapiro ** has been sanitized, e.g., shortened. 15794334Sgshapiro */ 15894334Sgshapiro 15994334Sgshapiro a->q_state = QS_BADADDR; 16090792Sgshapiro } 16190792Sgshapiro 16238032Speter /* 16338032Speter ** Make local copies of the host & user and then 16438032Speter ** transport them out. 16538032Speter */ 16638032Speter 16790792Sgshapiro allocaddr(a, flags, addr, e); 16864562Sgshapiro if (QS_IS_BADADDR(a->q_state)) 16994334Sgshapiro { 17094334Sgshapiro /* weed out bad characters in the printable address too */ 17194334Sgshapiro (void) hasctrlchar(a->q_paddr, isrcpt, false); 17238032Speter return a; 17394334Sgshapiro } 17438032Speter 17538032Speter /* 17690792Sgshapiro ** Select a queue directory for recipient addresses. 17790792Sgshapiro ** This is done here and in split_across_queue_groups(), 17890792Sgshapiro ** but the latter applies to addresses after aliasing, 17990792Sgshapiro ** and only if splitting is done. 18090792Sgshapiro */ 18190792Sgshapiro 18290792Sgshapiro if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) && 183168515Sgshapiro !bitset(RF_SENDERADDR|RF_HEADERADDR|RF_RM_ADDR, flags) && 18490792Sgshapiro OpMode != MD_INITALIAS) 18590792Sgshapiro { 18690792Sgshapiro int r; 18790792Sgshapiro 18890792Sgshapiro /* call ruleset which should return a queue group name */ 18990792Sgshapiro r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf, 19090792Sgshapiro sizeof(pvpbuf)); 19190792Sgshapiro if (r == EX_OK && 19290792Sgshapiro pvp != NULL && pvp[0] != NULL && 19390792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 19490792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 19590792Sgshapiro { 19690792Sgshapiro r = name2qid(pvp[1]); 19790792Sgshapiro if (r == NOQGRP && LogLevel > 10) 19890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 19990792Sgshapiro "can't find queue group name %s, selection ignored", 20090792Sgshapiro pvp[1]); 20190792Sgshapiro if (tTd(20, 4) && r != NOQGRP) 20290792Sgshapiro sm_syslog(LOG_INFO, NOQID, 20390792Sgshapiro "queue group name %s -> %d", 20490792Sgshapiro pvp[1], r); 20590792Sgshapiro a->q_qgrp = r == NOQGRP ? ENVQGRP : r; 20690792Sgshapiro } 20790792Sgshapiro } 20890792Sgshapiro 20990792Sgshapiro /* 21038032Speter ** If there was a parsing failure, mark it for queueing. 21138032Speter */ 21238032Speter 21364562Sgshapiro if (qup && OpMode != MD_INITALIAS) 21438032Speter { 21538032Speter char *msg = "Transient parse error -- message queued for future delivery"; 21638032Speter 21738032Speter if (e->e_sendmode == SM_DEFER) 21838032Speter msg = "Deferring message until queue run"; 21938032Speter if (tTd(20, 1)) 220173340Sgshapiro sm_dprintf("parseaddr: queueing message\n"); 22138032Speter message(msg); 22238032Speter if (e->e_message == NULL && e->e_sendmode != SM_DEFER) 22390792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, msg); 22464562Sgshapiro a->q_state = QS_QUEUEUP; 22538032Speter a->q_status = "4.4.3"; 22638032Speter } 22738032Speter 22838032Speter /* 22938032Speter ** Compute return value. 23038032Speter */ 23138032Speter 23238032Speter if (tTd(20, 1)) 23338032Speter { 23490792Sgshapiro sm_dprintf("parseaddr-->"); 235132943Sgshapiro printaddr(sm_debug_file(), a, false); 23638032Speter } 23738032Speter 23864562Sgshapiro return a; 23938032Speter} 24090792Sgshapiro/* 24190792Sgshapiro** INVALIDADDR -- check for address containing characters used for macros 24238032Speter** 24338032Speter** Parameters: 24438032Speter** addr -- the address to check. 245244833Sgshapiro** note: this is the complete address (including display part) 24690792Sgshapiro** delimptr -- if non-NULL: end of address to check, i.e., 24790792Sgshapiro** a pointer in the address string. 24890792Sgshapiro** isrcpt -- true iff the address is for a recipient. 24938032Speter** 25038032Speter** Returns: 25190792Sgshapiro** true -- if the address has characters that are reservered 25290792Sgshapiro** for macros or is too long. 25390792Sgshapiro** false -- otherwise. 25438032Speter*/ 25538032Speter 25638032Speterbool 25790792Sgshapiroinvalidaddr(addr, delimptr, isrcpt) 25838032Speter register char *addr; 25938032Speter char *delimptr; 26090792Sgshapiro bool isrcpt; 26138032Speter{ 26290792Sgshapiro bool result = false; 26338032Speter char savedelim = '\0'; 26494334Sgshapiro char *b = addr; 26590792Sgshapiro int len = 0; 26638032Speter 26738032Speter if (delimptr != NULL) 26838032Speter { 26990792Sgshapiro /* delimptr points to the end of the address to test */ 27038032Speter savedelim = *delimptr; 27190792Sgshapiro if (savedelim != '\0') /* if that isn't '\0' already: */ 27290792Sgshapiro *delimptr = '\0'; /* set it */ 27338032Speter } 27490792Sgshapiro for (; *addr != '\0'; addr++) 27538032Speter { 276168515Sgshapiro if (!EightBitAddrOK && (*addr & 0340) == 0200) 27790792Sgshapiro { 27890792Sgshapiro setstat(EX_USAGE); 27990792Sgshapiro result = true; 28094334Sgshapiro *addr = BAD_CHAR_REPLACEMENT; 28190792Sgshapiro } 28290792Sgshapiro if (++len > MAXNAME - 1) 28390792Sgshapiro { 28494334Sgshapiro char saved = *addr; 28594334Sgshapiro 28694334Sgshapiro *addr = '\0'; 28794334Sgshapiro usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)", 28894334Sgshapiro b, MAXNAME - 1); 28994334Sgshapiro *addr = saved; 29090792Sgshapiro result = true; 29190792Sgshapiro goto delim; 29290792Sgshapiro } 29338032Speter } 29490792Sgshapiro if (result) 29590792Sgshapiro { 29690792Sgshapiro if (isrcpt) 29794334Sgshapiro usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"", 29894334Sgshapiro b); 29990792Sgshapiro else 30094334Sgshapiro usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"", 30194334Sgshapiro b); 30290792Sgshapiro } 30390792Sgshapirodelim: 30490792Sgshapiro if (delimptr != NULL && savedelim != '\0') 30590792Sgshapiro *delimptr = savedelim; /* restore old character at delimptr */ 30690792Sgshapiro return result; 30790792Sgshapiro} 30890792Sgshapiro/* 30990792Sgshapiro** HASCTRLCHAR -- check for address containing meta-characters 31090792Sgshapiro** 31190792Sgshapiro** Checks that the address contains no meta-characters, and contains 31290792Sgshapiro** no "non-printable" characters unless they are quoted or escaped. 31390792Sgshapiro** Quoted or escaped characters are literals. 31490792Sgshapiro** 31590792Sgshapiro** Parameters: 31690792Sgshapiro** addr -- the address to check. 31790792Sgshapiro** isrcpt -- true if the address is for a recipient; false 31890792Sgshapiro** indicates a from. 31994334Sgshapiro** complain -- true if an error should issued if the address 32094334Sgshapiro** is invalid and should be "repaired". 32190792Sgshapiro** 32290792Sgshapiro** Returns: 323223067Sgshapiro** true -- if the address has any "weird" characters or 32490792Sgshapiro** non-printable characters or if a quote is unbalanced. 32590792Sgshapiro** false -- otherwise. 32690792Sgshapiro*/ 32790792Sgshapiro 32890792Sgshapirostatic bool 32994334Sgshapirohasctrlchar(addr, isrcpt, complain) 33090792Sgshapiro register char *addr; 33194334Sgshapiro bool isrcpt, complain; 33290792Sgshapiro{ 33394334Sgshapiro bool quoted = false; 33490792Sgshapiro int len = 0; 33594334Sgshapiro char *result = NULL; 33694334Sgshapiro char *b = addr; 33790792Sgshapiro 33890792Sgshapiro if (addr == NULL) 33990792Sgshapiro return false; 34038032Speter for (; *addr != '\0'; addr++) 34138032Speter { 34294334Sgshapiro if (++len > MAXNAME - 1) 34394334Sgshapiro { 34494334Sgshapiro if (complain) 34594334Sgshapiro { 34694334Sgshapiro (void) shorten_rfc822_string(b, MAXNAME - 1); 34794334Sgshapiro usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)", 34894334Sgshapiro b, MAXNAME - 1); 34994334Sgshapiro return true; 35094334Sgshapiro } 35194334Sgshapiro result = "too long"; 35294334Sgshapiro } 353168515Sgshapiro if (!EightBitAddrOK && !quoted && (*addr < 32 || *addr == 127)) 35490792Sgshapiro { 35594334Sgshapiro result = "non-printable character"; 35694334Sgshapiro *addr = BAD_CHAR_REPLACEMENT; 35794334Sgshapiro continue; 35890792Sgshapiro } 35990792Sgshapiro if (*addr == '"') 36090792Sgshapiro quoted = !quoted; 36190792Sgshapiro else if (*addr == '\\') 36290792Sgshapiro { 36390792Sgshapiro /* XXX Generic problem: no '\0' in strings. */ 36490792Sgshapiro if (*++addr == '\0') 36590792Sgshapiro { 36694334Sgshapiro result = "trailing \\ character"; 36794334Sgshapiro *--addr = BAD_CHAR_REPLACEMENT; 36890792Sgshapiro break; 36990792Sgshapiro } 37090792Sgshapiro } 371168515Sgshapiro if (!EightBitAddrOK && (*addr & 0340) == 0200) 37290792Sgshapiro { 37390792Sgshapiro setstat(EX_USAGE); 37494334Sgshapiro result = "8-bit character"; 37594334Sgshapiro *addr = BAD_CHAR_REPLACEMENT; 37694334Sgshapiro continue; 37790792Sgshapiro } 37838032Speter } 37990792Sgshapiro if (quoted) 38094334Sgshapiro result = "unbalanced quote"; /* unbalanced quote */ 38194334Sgshapiro if (result != NULL && complain) 38238032Speter { 38390792Sgshapiro if (isrcpt) 38494334Sgshapiro usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)", 38594334Sgshapiro b, result); 38690792Sgshapiro else 38794334Sgshapiro usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)", 38894334Sgshapiro b, result); 38938032Speter } 39094334Sgshapiro return result != NULL; 39138032Speter} 39290792Sgshapiro/* 39338032Speter** ALLOCADDR -- do local allocations of address on demand. 39438032Speter** 39538032Speter** Also lowercases the host name if requested. 39638032Speter** 39738032Speter** Parameters: 39838032Speter** a -- the address to reallocate. 39938032Speter** flags -- the copy flag (see RF_ definitions in sendmail.h 40038032Speter** for a description). 40138032Speter** paddr -- the printname of the address. 40290792Sgshapiro** e -- envelope 40338032Speter** 40438032Speter** Returns: 40538032Speter** none. 40638032Speter** 40738032Speter** Side Effects: 40838032Speter** Copies portions of a into local buffers as requested. 40938032Speter*/ 41038032Speter 41164562Sgshapirostatic void 41290792Sgshapiroallocaddr(a, flags, paddr, e) 41338032Speter register ADDRESS *a; 41438032Speter int flags; 41538032Speter char *paddr; 41690792Sgshapiro ENVELOPE *e; 41738032Speter{ 41838032Speter if (tTd(24, 4)) 41990792Sgshapiro sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr); 42038032Speter 42138032Speter a->q_paddr = paddr; 42238032Speter 42338032Speter if (a->q_user == NULL) 42490792Sgshapiro a->q_user = ""; 42538032Speter if (a->q_host == NULL) 42690792Sgshapiro a->q_host = ""; 42738032Speter 42838032Speter if (bitset(RF_COPYPARSE, flags)) 42938032Speter { 43090792Sgshapiro a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host); 43138032Speter if (a->q_user != a->q_paddr) 43290792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user); 43338032Speter } 43438032Speter 43538032Speter if (a->q_paddr == NULL) 43690792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 43790792Sgshapiro a->q_qgrp = NOAQGRP; 43838032Speter} 439168515Sgshapiro 44090792Sgshapiro/* 44138032Speter** PRESCAN -- Prescan name and make it canonical 44238032Speter** 44338032Speter** Scans a name and turns it into a set of tokens. This process 44464562Sgshapiro** deletes blanks and comments (in parentheses) (if the token type 44564562Sgshapiro** for left paren is SPC). 44638032Speter** 44738032Speter** This routine knows about quoted strings and angle brackets. 44838032Speter** 44938032Speter** There are certain subtleties to this routine. The one that 45038032Speter** comes to mind now is that backslashes on the ends of names 45138032Speter** are silently stripped off; this is intentional. The problem 45238032Speter** is that some versions of sndmsg (like at LBL) set the kill 45338032Speter** character to something other than @ when reading addresses; 45438032Speter** so people type "csvax.eric\@berkeley" -- which screws up the 45538032Speter** berknet mailer. 45638032Speter** 45738032Speter** Parameters: 45838032Speter** addr -- the name to chomp. 45938032Speter** delim -- the delimiter for the address, normally 46038032Speter** '\0' or ','; \0 is accepted in any case. 46138032Speter** If '\t' then we are reading the .cf file. 46238032Speter** pvpbuf -- place to put the saved text -- note that 46338032Speter** the pointers are static. 46438032Speter** pvpbsize -- size of pvpbuf. 46538032Speter** delimptr -- if non-NULL, set to the location of the 46638032Speter** terminating delimiter. 46738032Speter** toktab -- if set, a token table to use for parsing. 46838032Speter** If NULL, use the default table. 469132943Sgshapiro** ignore -- if true, ignore unbalanced addresses 47038032Speter** 47138032Speter** Returns: 47238032Speter** A pointer to a vector of tokens. 47338032Speter** NULL on error. 47438032Speter*/ 47538032Speter 47638032Speter/* states and character types */ 47764562Sgshapiro#define OPR 0 /* operator */ 47864562Sgshapiro#define ATM 1 /* atom */ 47964562Sgshapiro#define QST 2 /* in quoted string */ 48064562Sgshapiro#define SPC 3 /* chewing up spaces */ 48164562Sgshapiro#define ONE 4 /* pick up one character */ 48264562Sgshapiro#define ILL 5 /* illegal character */ 48338032Speter 48464562Sgshapiro#define NSTATES 6 /* number of states */ 48564562Sgshapiro#define TYPE 017 /* mask to select state type */ 48638032Speter 48738032Speter/* meta bits for table */ 48864562Sgshapiro#define M 020 /* meta character; don't pass through */ 48964562Sgshapiro#define B 040 /* cause a break */ 49064562Sgshapiro#define MB M|B /* meta-break */ 49138032Speter 49238032Speterstatic short StateTab[NSTATES][NSTATES] = 49338032Speter{ 49438032Speter /* oldst chtype> OPR ATM QST SPC ONE ILL */ 49538032Speter /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, 49638032Speter /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, 49738032Speter /*QST*/ { QST, QST, OPR, QST, QST, QST }, 49838032Speter /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, 49938032Speter /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, 500168515Sgshapiro /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M } 50138032Speter}; 50238032Speter 503168515Sgshapiro/* these all get modified with the OperatorChars */ 504168515Sgshapiro 505168515Sgshapiro/* token type table for external strings */ 506168515Sgshapirounsigned char ExtTokenTab[256] = 50738032Speter{ 50838032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 50938032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 51038032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 51138032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 51238032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 51364562Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 51438032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 51538032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 51638032Speter /* @ A B C D E F G H I J K L M N O */ 51738032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 51838032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 51938032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52038032Speter /* ` a b c d e f g h i j k l m n o */ 52138032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52238032Speter /* p q r s t u v w x y z { | } ~ del */ 52338032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 52438032Speter 52538032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 526168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 527168515Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 528168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 529168515Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 530168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 531168515Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 532168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 533168515Sgshapiro /* @ A B C D E F G H I J K L M N O */ 534168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 535168515Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 536168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 537168515Sgshapiro /* ` a b c d e f g h i j k l m n o */ 538168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 539168515Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 540168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM 541168515Sgshapiro}; 542168515Sgshapiro 543168515Sgshapiro/* token type table for internal strings */ 544168515Sgshapirounsigned char IntTokenTab[256] = 545168515Sgshapiro{ 546168515Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 547168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 548168515Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 549168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 550168515Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 551168515Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 552168515Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 553168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 554168515Sgshapiro /* @ A B C D E F G H I J K L M N O */ 555168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 556168515Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 557168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 558168515Sgshapiro /* ` a b c d e f g h i j k l m n o */ 559168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 560168515Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 561168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 562168515Sgshapiro 563168515Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 56438032Speter OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 56538032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 56638032Speter OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 56738032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 56838032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 56938032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 57038032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 57138032Speter /* @ A B C D E F G H I J K L M N O */ 57238032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 57338032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 57438032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 57538032Speter /* ` a b c d e f g h i j k l m n o */ 57638032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 57738032Speter /* p q r s t u v w x y z { | } ~ del */ 578168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE 57938032Speter}; 58038032Speter 58138032Speter/* token type table for MIME parsing */ 58290792Sgshapirounsigned char MimeTokenTab[256] = 58338032Speter{ 58438032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 58538032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL, 58638032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 58738032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 58838032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 58964562Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR, 59038032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 59138032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR, 59238032Speter /* @ A B C D E F G H I J K L M N O */ 59338032Speter OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 59438032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 59538032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM, 59638032Speter /* ` a b c d e f g h i j k l m n o */ 59738032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 59838032Speter /* p q r s t u v w x y z { | } ~ del */ 59938032Speter ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 60038032Speter 60138032Speter /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 60238032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 60338032Speter /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 60438032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 60538032Speter /* sp ! " # $ % & ' ( ) * + , - . / */ 60638032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 60738032Speter /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 60838032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 60938032Speter /* @ A B C D E F G H I J K L M N O */ 61038032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61138032Speter /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 61238032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61338032Speter /* ` a b c d e f g h i j k l m n o */ 61438032Speter ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 61538032Speter /* p q r s t u v w x y z { | } ~ del */ 616168515Sgshapiro ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ONE 61738032Speter}; 61838032Speter 61964562Sgshapiro/* token type table: don't strip comments */ 62090792Sgshapirounsigned char TokTypeNoC[256] = 62164562Sgshapiro{ 62264562Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 62364562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 62464562Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 62564562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 62664562Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 62764562Sgshapiro SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM, 62864562Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 62964562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63064562Sgshapiro /* @ A B C D E F G H I J K L M N O */ 63164562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63264562Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 63364562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63464562Sgshapiro /* ` a b c d e f g h i j k l m n o */ 63564562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63664562Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 63764562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 63838032Speter 63964562Sgshapiro /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 64064562Sgshapiro OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 64164562Sgshapiro /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 64264562Sgshapiro OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 64364562Sgshapiro /* sp ! " # $ % & ' ( ) * + , - . / */ 64464562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64564562Sgshapiro /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 64664562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64764562Sgshapiro /* @ A B C D E F G H I J K L M N O */ 64864562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 64964562Sgshapiro /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 65064562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 65164562Sgshapiro /* ` a b c d e f g h i j k l m n o */ 65264562Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 65364562Sgshapiro /* p q r s t u v w x y z { | } ~ del */ 654168515Sgshapiro ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE 65564562Sgshapiro}; 65638032Speter 65764562Sgshapiro 658112810Sgshapiro#define NOCHAR (-1) /* signal nothing in lookahead token */ 65964562Sgshapiro 66038032Speterchar ** 661132943Sgshapiroprescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore) 66238032Speter char *addr; 66338032Speter int delim; 66438032Speter char pvpbuf[]; 66538032Speter int pvpbsize; 66638032Speter char **delimptr; 66790792Sgshapiro unsigned char *toktab; 668132943Sgshapiro bool ignore; 66938032Speter{ 67038032Speter register char *p; 67138032Speter register char *q; 67238032Speter register int c; 67338032Speter char **avp; 67438032Speter bool bslashmode; 67538032Speter bool route_syntax; 67638032Speter int cmntcnt; 67738032Speter int anglecnt; 67838032Speter char *tok; 67938032Speter int state; 68038032Speter int newstate; 68138032Speter char *saveto = CurEnv->e_to; 68264562Sgshapiro static char *av[MAXATOM + 1]; 68390792Sgshapiro static bool firsttime = true; 68438032Speter 68538032Speter if (firsttime) 68638032Speter { 68738032Speter /* initialize the token type table */ 68838032Speter char obuf[50]; 68938032Speter 69090792Sgshapiro firsttime = false; 69138032Speter if (OperatorChars == NULL) 69238032Speter { 69338032Speter if (ConfigLevel < 7) 69438032Speter OperatorChars = macvalue('o', CurEnv); 69538032Speter if (OperatorChars == NULL) 69638032Speter OperatorChars = ".:@[]"; 69738032Speter } 698168515Sgshapiro expand(OperatorChars, obuf, sizeof(obuf) - sizeof(DELIMCHARS), 69964562Sgshapiro CurEnv); 700168515Sgshapiro (void) sm_strlcat(obuf, DELIMCHARS, sizeof(obuf)); 70138032Speter for (p = obuf; *p != '\0'; p++) 70238032Speter { 703168515Sgshapiro if (IntTokenTab[*p & 0xff] == ATM) 704168515Sgshapiro IntTokenTab[*p & 0xff] = OPR; 705168515Sgshapiro if (ExtTokenTab[*p & 0xff] == ATM) 706168515Sgshapiro ExtTokenTab[*p & 0xff] = OPR; 70764562Sgshapiro if (TokTypeNoC[*p & 0xff] == ATM) 70864562Sgshapiro TokTypeNoC[*p & 0xff] = OPR; 70938032Speter } 71038032Speter } 71138032Speter if (toktab == NULL) 712168515Sgshapiro toktab = ExtTokenTab; 71338032Speter 71438032Speter /* make sure error messages don't have garbage on them */ 71538032Speter errno = 0; 71638032Speter 71738032Speter q = pvpbuf; 71890792Sgshapiro bslashmode = false; 71990792Sgshapiro route_syntax = false; 72038032Speter cmntcnt = 0; 72138032Speter anglecnt = 0; 72238032Speter avp = av; 72338032Speter state = ATM; 72438032Speter c = NOCHAR; 72538032Speter p = addr; 72638032Speter CurEnv->e_to = p; 72738032Speter if (tTd(22, 11)) 72838032Speter { 72990792Sgshapiro sm_dprintf("prescan: "); 730132943Sgshapiro xputs(sm_debug_file(), p); 73190792Sgshapiro sm_dprintf("\n"); 73238032Speter } 73338032Speter 73438032Speter do 73538032Speter { 73638032Speter /* read a token */ 73738032Speter tok = q; 73838032Speter for (;;) 73938032Speter { 74038032Speter /* store away any old lookahead character */ 74138032Speter if (c != NOCHAR && !bslashmode) 74238032Speter { 74338032Speter /* see if there is room */ 74438032Speter if (q >= &pvpbuf[pvpbsize - 5]) 74538032Speter { 746112810Sgshapiro addrtoolong: 74764562Sgshapiro usrerr("553 5.1.1 Address too long"); 74890792Sgshapiro if (strlen(addr) > MAXNAME) 74938032Speter addr[MAXNAME] = '\0'; 75038032Speter returnnull: 75138032Speter if (delimptr != NULL) 752120169Snectar { 753120169Snectar if (p > addr) 754120256Sgshapiro --p; 75538032Speter *delimptr = p; 756120169Snectar } 75738032Speter CurEnv->e_to = saveto; 75864562Sgshapiro return NULL; 75938032Speter } 76038032Speter 76138032Speter /* squirrel it away */ 762112810Sgshapiro#if !ALLOW_255 763168515Sgshapiro if ((char) c == (char) -1 && !tTd(82, 101) && 764168515Sgshapiro !EightBitAddrOK) 765112810Sgshapiro c &= 0x7f; 766112810Sgshapiro#endif /* !ALLOW_255 */ 76738032Speter *q++ = c; 76838032Speter } 76938032Speter 77038032Speter /* read a new input character */ 771112810Sgshapiro c = (*p++) & 0x00ff; 77238032Speter if (c == '\0') 77338032Speter { 77438032Speter /* diagnose and patch up bad syntax */ 775132943Sgshapiro if (ignore) 776132943Sgshapiro break; 777132943Sgshapiro else if (state == QST) 77838032Speter { 77990792Sgshapiro usrerr("553 Unbalanced '\"'"); 78038032Speter c = '"'; 78138032Speter } 78238032Speter else if (cmntcnt > 0) 78338032Speter { 78490792Sgshapiro usrerr("553 Unbalanced '('"); 78538032Speter c = ')'; 78638032Speter } 78738032Speter else if (anglecnt > 0) 78838032Speter { 78938032Speter c = '>'; 79090792Sgshapiro usrerr("553 Unbalanced '<'"); 79138032Speter } 79238032Speter else 79338032Speter break; 79438032Speter 79538032Speter p--; 79638032Speter } 79738032Speter else if (c == delim && cmntcnt <= 0 && state != QST) 79838032Speter { 79938032Speter if (anglecnt <= 0) 80038032Speter break; 80138032Speter 80238032Speter /* special case for better error management */ 803132943Sgshapiro if (delim == ',' && !route_syntax && !ignore) 80438032Speter { 80590792Sgshapiro usrerr("553 Unbalanced '<'"); 80638032Speter c = '>'; 80738032Speter p--; 80838032Speter } 80938032Speter } 81038032Speter 81138032Speter if (tTd(22, 101)) 81290792Sgshapiro sm_dprintf("c=%c, s=%d; ", c, state); 81338032Speter 81438032Speter /* chew up special characters */ 81538032Speter *q = '\0'; 81638032Speter if (bslashmode) 81738032Speter { 81890792Sgshapiro bslashmode = false; 81938032Speter 82038032Speter /* kludge \! for naive users */ 82138032Speter if (cmntcnt > 0) 82238032Speter { 82338032Speter c = NOCHAR; 82438032Speter continue; 82538032Speter } 82638032Speter else if (c != '!' || state == QST) 82738032Speter { 828112810Sgshapiro /* see if there is room */ 829112810Sgshapiro if (q >= &pvpbuf[pvpbsize - 5]) 830112810Sgshapiro goto addrtoolong; 83138032Speter *q++ = '\\'; 83238032Speter continue; 83338032Speter } 83438032Speter } 83538032Speter 83638032Speter if (c == '\\') 83738032Speter { 83890792Sgshapiro bslashmode = true; 83938032Speter } 84038032Speter else if (state == QST) 84138032Speter { 84264562Sgshapiro /* EMPTY */ 84338032Speter /* do nothing, just avoid next clauses */ 84438032Speter } 84564562Sgshapiro else if (c == '(' && toktab['('] == SPC) 84638032Speter { 84738032Speter cmntcnt++; 84838032Speter c = NOCHAR; 84938032Speter } 85064562Sgshapiro else if (c == ')' && toktab['('] == SPC) 85138032Speter { 85238032Speter if (cmntcnt <= 0) 85338032Speter { 854132943Sgshapiro if (!ignore) 855132943Sgshapiro { 856132943Sgshapiro usrerr("553 Unbalanced ')'"); 857132943Sgshapiro c = NOCHAR; 858132943Sgshapiro } 85938032Speter } 86038032Speter else 86138032Speter cmntcnt--; 86238032Speter } 86338032Speter else if (cmntcnt > 0) 86464562Sgshapiro { 86538032Speter c = NOCHAR; 86664562Sgshapiro } 86738032Speter else if (c == '<') 86838032Speter { 86964562Sgshapiro char *ptr = p; 87038032Speter 87138032Speter anglecnt++; 87264562Sgshapiro while (isascii(*ptr) && isspace(*ptr)) 87364562Sgshapiro ptr++; 87464562Sgshapiro if (*ptr == '@') 87590792Sgshapiro route_syntax = true; 87638032Speter } 87738032Speter else if (c == '>') 87838032Speter { 87938032Speter if (anglecnt <= 0) 88038032Speter { 881132943Sgshapiro if (!ignore) 882132943Sgshapiro { 883132943Sgshapiro usrerr("553 Unbalanced '>'"); 884132943Sgshapiro c = NOCHAR; 885132943Sgshapiro } 88638032Speter } 88738032Speter else 88838032Speter anglecnt--; 88990792Sgshapiro route_syntax = false; 89038032Speter } 89138032Speter else if (delim == ' ' && isascii(c) && isspace(c)) 89238032Speter c = ' '; 89338032Speter 89438032Speter if (c == NOCHAR) 89538032Speter continue; 89638032Speter 89738032Speter /* see if this is end of input */ 89838032Speter if (c == delim && anglecnt <= 0 && state != QST) 89938032Speter break; 90038032Speter 90138032Speter newstate = StateTab[state][toktab[c & 0xff]]; 90238032Speter if (tTd(22, 101)) 90390792Sgshapiro sm_dprintf("ns=%02o\n", newstate); 90438032Speter state = newstate & TYPE; 90538032Speter if (state == ILL) 90638032Speter { 90738032Speter if (isascii(c) && isprint(c)) 90890792Sgshapiro usrerr("553 Illegal character %c", c); 90938032Speter else 91090792Sgshapiro usrerr("553 Illegal character 0x%02x", 91190792Sgshapiro c & 0x0ff); 91238032Speter } 91338032Speter if (bitset(M, newstate)) 91438032Speter c = NOCHAR; 91538032Speter if (bitset(B, newstate)) 91638032Speter break; 91738032Speter } 91838032Speter 91938032Speter /* new token */ 92038032Speter if (tok != q) 92138032Speter { 922112810Sgshapiro /* see if there is room */ 923112810Sgshapiro if (q >= &pvpbuf[pvpbsize - 5]) 924112810Sgshapiro goto addrtoolong; 92538032Speter *q++ = '\0'; 92638032Speter if (tTd(22, 36)) 92738032Speter { 92890792Sgshapiro sm_dprintf("tok="); 929132943Sgshapiro xputs(sm_debug_file(), tok); 93090792Sgshapiro sm_dprintf("\n"); 93138032Speter } 93238032Speter if (avp >= &av[MAXATOM]) 93338032Speter { 93464562Sgshapiro usrerr("553 5.1.0 prescan: too many tokens"); 93538032Speter goto returnnull; 93638032Speter } 93738032Speter if (q - tok > MAXNAME) 93838032Speter { 93964562Sgshapiro usrerr("553 5.1.0 prescan: token too long"); 94038032Speter goto returnnull; 94138032Speter } 94238032Speter *avp++ = tok; 94338032Speter } 94438032Speter } while (c != '\0' && (c != delim || anglecnt > 0)); 94538032Speter *avp = NULL; 94638032Speter if (delimptr != NULL) 947120256Sgshapiro { 948120256Sgshapiro if (p > addr) 949120256Sgshapiro p--; 95038032Speter *delimptr = p; 951120256Sgshapiro } 95238032Speter if (tTd(22, 12)) 95338032Speter { 95490792Sgshapiro sm_dprintf("prescan==>"); 955132943Sgshapiro printav(sm_debug_file(), av); 95638032Speter } 95738032Speter CurEnv->e_to = saveto; 95838032Speter if (av[0] == NULL) 95938032Speter { 96038032Speter if (tTd(22, 1)) 96190792Sgshapiro sm_dprintf("prescan: null leading token\n"); 96264562Sgshapiro return NULL; 96338032Speter } 96464562Sgshapiro return av; 96538032Speter} 96690792Sgshapiro/* 96738032Speter** REWRITE -- apply rewrite rules to token vector. 96838032Speter** 96938032Speter** This routine is an ordered production system. Each rewrite 97038032Speter** rule has a LHS (called the pattern) and a RHS (called the 97138032Speter** rewrite); 'rwr' points the the current rewrite rule. 97238032Speter** 97338032Speter** For each rewrite rule, 'avp' points the address vector we 97438032Speter** are trying to match against, and 'pvp' points to the pattern. 97538032Speter** If pvp points to a special match value (MATCHZANY, MATCHANY, 97638032Speter** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 97738032Speter** matched is saved away in the match vector (pointed to by 'mvp'). 97838032Speter** 97938032Speter** When a match between avp & pvp does not match, we try to 98038032Speter** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 98138032Speter** we must also back out the match in mvp. If we reach a 98238032Speter** MATCHANY or MATCHZANY we just extend the match and start 98338032Speter** over again. 98438032Speter** 98538032Speter** When we finally match, we rewrite the address vector 98638032Speter** and try over again. 98738032Speter** 98838032Speter** Parameters: 98938032Speter** pvp -- pointer to token vector. 99038032Speter** ruleset -- the ruleset to use for rewriting. 99138032Speter** reclevel -- recursion level (to catch loops). 99238032Speter** e -- the current envelope. 99390792Sgshapiro** maxatom -- maximum length of buffer (usually MAXATOM) 99438032Speter** 99538032Speter** Returns: 99638032Speter** A status code. If EX_TEMPFAIL, higher level code should 99738032Speter** attempt recovery. 99838032Speter** 99938032Speter** Side Effects: 100038032Speter** pvp is modified. 100138032Speter*/ 100238032Speter 100338032Speterstruct match 100438032Speter{ 100564562Sgshapiro char **match_first; /* first token matched */ 100664562Sgshapiro char **match_last; /* last token matched */ 100764562Sgshapiro char **match_pattern; /* pointer to pattern */ 100838032Speter}; 100938032Speter 101038032Speterint 101190792Sgshapirorewrite(pvp, ruleset, reclevel, e, maxatom) 101238032Speter char **pvp; 101338032Speter int ruleset; 101438032Speter int reclevel; 101538032Speter register ENVELOPE *e; 101690792Sgshapiro int maxatom; 101738032Speter{ 101838032Speter register char *ap; /* address pointer */ 101938032Speter register char *rp; /* rewrite pointer */ 102064562Sgshapiro register char *rulename; /* ruleset name */ 102164562Sgshapiro register char *prefix; 102238032Speter register char **avp; /* address vector pointer */ 102338032Speter register char **rvp; /* rewrite vector pointer */ 102438032Speter register struct match *mlp; /* cur ptr into mlist */ 102538032Speter register struct rewrite *rwr; /* pointer to current rewrite rule */ 102638032Speter int ruleno; /* current rule number */ 102738032Speter int rstat = EX_OK; /* return status */ 102838032Speter int loopcount; 102938032Speter struct match mlist[MAXMATCH]; /* stores match on LHS */ 103064562Sgshapiro char *npvp[MAXATOM + 1]; /* temporary space for rebuild */ 103138032Speter char buf[MAXLINE]; 103264562Sgshapiro char name[6]; 103338032Speter 1034120256Sgshapiro /* 1035120256Sgshapiro ** mlp will not exceed mlist[] because readcf enforces 1036120256Sgshapiro ** the upper limit of entries when reading rulesets. 1037120256Sgshapiro */ 1038120256Sgshapiro 103964562Sgshapiro if (ruleset < 0 || ruleset >= MAXRWSETS) 104038032Speter { 104164562Sgshapiro syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset); 104264562Sgshapiro return EX_CONFIG; 104364562Sgshapiro } 104464562Sgshapiro rulename = RuleSetNames[ruleset]; 104564562Sgshapiro if (rulename == NULL) 104664562Sgshapiro { 1047168515Sgshapiro (void) sm_snprintf(name, sizeof(name), "%d", ruleset); 104864562Sgshapiro rulename = name; 104964562Sgshapiro } 105064562Sgshapiro if (OpMode == MD_TEST) 105164562Sgshapiro prefix = ""; 105264562Sgshapiro else 105364562Sgshapiro prefix = "rewrite: ruleset "; 105464562Sgshapiro if (OpMode == MD_TEST) 105564562Sgshapiro { 105690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 105790792Sgshapiro "%s%-16.16s input:", prefix, rulename); 1058132943Sgshapiro printav(smioout, pvp); 105938032Speter } 106064562Sgshapiro else if (tTd(21, 1)) 106138032Speter { 106290792Sgshapiro sm_dprintf("%s%-16.16s input:", prefix, rulename); 1063132943Sgshapiro printav(sm_debug_file(), pvp); 106438032Speter } 106538032Speter if (reclevel++ > MaxRuleRecursion) 106638032Speter { 106764562Sgshapiro syserr("rewrite: excessive recursion (max %d), ruleset %s", 106864562Sgshapiro MaxRuleRecursion, rulename); 106938032Speter return EX_CONFIG; 107038032Speter } 107138032Speter if (pvp == NULL) 107238032Speter return EX_USAGE; 1073120256Sgshapiro if (maxatom <= 0) 1074120256Sgshapiro return EX_USAGE; 107538032Speter 107638032Speter /* 107738032Speter ** Run through the list of rewrite rules, applying 107838032Speter ** any that match. 107938032Speter */ 108038032Speter 108138032Speter ruleno = 1; 108238032Speter loopcount = 0; 108338032Speter for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 108438032Speter { 108564562Sgshapiro int status; 108638032Speter 108738032Speter /* if already canonical, quit now */ 108838032Speter if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET) 108938032Speter break; 109038032Speter 109138032Speter if (tTd(21, 12)) 109238032Speter { 109364562Sgshapiro if (tTd(21, 15)) 109490792Sgshapiro sm_dprintf("-----trying rule (line %d):", 109564562Sgshapiro rwr->r_line); 109664562Sgshapiro else 109790792Sgshapiro sm_dprintf("-----trying rule:"); 1098132943Sgshapiro printav(sm_debug_file(), rwr->r_lhs); 109938032Speter } 110038032Speter 110138032Speter /* try to match on this rule */ 110238032Speter mlp = mlist; 110338032Speter rvp = rwr->r_lhs; 110438032Speter avp = pvp; 110538032Speter if (++loopcount > 100) 110638032Speter { 110764562Sgshapiro syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d", 110864562Sgshapiro rulename, ruleno); 110938032Speter if (tTd(21, 1)) 111038032Speter { 111190792Sgshapiro sm_dprintf("workspace: "); 1112132943Sgshapiro printav(sm_debug_file(), pvp); 111338032Speter } 111438032Speter break; 111538032Speter } 111638032Speter 111738032Speter while ((ap = *avp) != NULL || *rvp != NULL) 111838032Speter { 111938032Speter rp = *rvp; 112038032Speter if (tTd(21, 35)) 112138032Speter { 112290792Sgshapiro sm_dprintf("ADVANCE rp="); 1123132943Sgshapiro xputs(sm_debug_file(), rp); 112490792Sgshapiro sm_dprintf(", ap="); 1125132943Sgshapiro xputs(sm_debug_file(), ap); 112690792Sgshapiro sm_dprintf("\n"); 112738032Speter } 112838032Speter if (rp == NULL) 112938032Speter { 113038032Speter /* end-of-pattern before end-of-address */ 113138032Speter goto backup; 113238032Speter } 1133168515Sgshapiro if (ap == NULL && 1134168515Sgshapiro (rp[0] & 0377) != MATCHZANY && 1135168515Sgshapiro (rp[0] & 0377) != MATCHZERO) 113638032Speter { 113738032Speter /* end-of-input with patterns left */ 113838032Speter goto backup; 113938032Speter } 114038032Speter 1141168515Sgshapiro switch (rp[0] & 0377) 114238032Speter { 114338032Speter case MATCHCLASS: 114438032Speter /* match any phrase in a class */ 114564562Sgshapiro mlp->match_pattern = rvp; 114664562Sgshapiro mlp->match_first = avp; 114738032Speter extendclass: 114838032Speter ap = *avp; 114938032Speter if (ap == NULL) 115038032Speter goto backup; 115164562Sgshapiro mlp->match_last = avp++; 115264562Sgshapiro cataddr(mlp->match_first, mlp->match_last, 1153168515Sgshapiro buf, sizeof(buf), '\0', true); 115438032Speter if (!wordinclass(buf, rp[1])) 115538032Speter { 115638032Speter if (tTd(21, 36)) 115738032Speter { 115890792Sgshapiro sm_dprintf("EXTEND rp="); 1159132943Sgshapiro xputs(sm_debug_file(), rp); 116090792Sgshapiro sm_dprintf(", ap="); 1161132943Sgshapiro xputs(sm_debug_file(), ap); 116290792Sgshapiro sm_dprintf("\n"); 116338032Speter } 116438032Speter goto extendclass; 116538032Speter } 116638032Speter if (tTd(21, 36)) 116790792Sgshapiro sm_dprintf("CLMATCH\n"); 116838032Speter mlp++; 116938032Speter break; 117038032Speter 117138032Speter case MATCHNCLASS: 117238032Speter /* match any token not in a class */ 117338032Speter if (wordinclass(ap, rp[1])) 117438032Speter goto backup; 117538032Speter 117664562Sgshapiro /* FALLTHROUGH */ 117738032Speter 117838032Speter case MATCHONE: 117938032Speter case MATCHANY: 118038032Speter /* match exactly one token */ 118164562Sgshapiro mlp->match_pattern = rvp; 118264562Sgshapiro mlp->match_first = avp; 118364562Sgshapiro mlp->match_last = avp++; 118438032Speter mlp++; 118538032Speter break; 118638032Speter 118738032Speter case MATCHZANY: 118838032Speter /* match zero or more tokens */ 118964562Sgshapiro mlp->match_pattern = rvp; 119064562Sgshapiro mlp->match_first = avp; 119164562Sgshapiro mlp->match_last = avp - 1; 119238032Speter mlp++; 119338032Speter break; 119438032Speter 119538032Speter case MATCHZERO: 119638032Speter /* match zero tokens */ 119738032Speter break; 119838032Speter 119938032Speter case MACRODEXPAND: 120038032Speter /* 120138032Speter ** Match against run-time macro. 120238032Speter ** This algorithm is broken for the 120338032Speter ** general case (no recursive macros, 120438032Speter ** improper tokenization) but should 120538032Speter ** work for the usual cases. 120638032Speter */ 120738032Speter 120838032Speter ap = macvalue(rp[1], e); 120964562Sgshapiro mlp->match_first = avp; 121038032Speter if (tTd(21, 2)) 121198841Sgshapiro sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n", 121238032Speter macname(rp[1]), 121338032Speter ap == NULL ? "(NULL)" : ap); 121438032Speter 121538032Speter if (ap == NULL) 121638032Speter break; 121738032Speter while (*ap != '\0') 121838032Speter { 121938032Speter if (*avp == NULL || 122090792Sgshapiro sm_strncasecmp(ap, *avp, 122190792Sgshapiro strlen(*avp)) != 0) 122238032Speter { 122338032Speter /* no match */ 122464562Sgshapiro avp = mlp->match_first; 122538032Speter goto backup; 122638032Speter } 122738032Speter ap += strlen(*avp++); 122838032Speter } 122938032Speter 123038032Speter /* match */ 123138032Speter break; 123238032Speter 123338032Speter default: 123438032Speter /* must have exact match */ 123538032Speter if (sm_strcasecmp(rp, ap)) 123638032Speter goto backup; 123738032Speter avp++; 123838032Speter break; 123938032Speter } 124038032Speter 124138032Speter /* successful match on this token */ 124238032Speter rvp++; 124338032Speter continue; 124438032Speter 124538032Speter backup: 124638032Speter /* match failed -- back up */ 124738032Speter while (--mlp >= mlist) 124838032Speter { 124964562Sgshapiro rvp = mlp->match_pattern; 125038032Speter rp = *rvp; 125164562Sgshapiro avp = mlp->match_last + 1; 125238032Speter ap = *avp; 125338032Speter 125438032Speter if (tTd(21, 36)) 125538032Speter { 125690792Sgshapiro sm_dprintf("BACKUP rp="); 1257132943Sgshapiro xputs(sm_debug_file(), rp); 125890792Sgshapiro sm_dprintf(", ap="); 1259132943Sgshapiro xputs(sm_debug_file(), ap); 126090792Sgshapiro sm_dprintf("\n"); 126138032Speter } 126238032Speter 126338032Speter if (ap == NULL) 126438032Speter { 126538032Speter /* run off the end -- back up again */ 126638032Speter continue; 126738032Speter } 1268168515Sgshapiro 1269168515Sgshapiro if ((rp[0] & 0377) == MATCHANY || 1270168515Sgshapiro (rp[0] & 0377) == MATCHZANY) 127138032Speter { 127238032Speter /* extend binding and continue */ 127364562Sgshapiro mlp->match_last = avp++; 127438032Speter rvp++; 127538032Speter mlp++; 127638032Speter break; 127738032Speter } 1278168515Sgshapiro if ((rp[0] & 0377) == MATCHCLASS) 127938032Speter { 128038032Speter /* extend binding and try again */ 128164562Sgshapiro mlp->match_last = avp; 128238032Speter goto extendclass; 128338032Speter } 128438032Speter } 128538032Speter 128638032Speter if (mlp < mlist) 128738032Speter { 128838032Speter /* total failure to match */ 128938032Speter break; 129038032Speter } 129138032Speter } 129238032Speter 129338032Speter /* 129438032Speter ** See if we successfully matched 129538032Speter */ 129638032Speter 129738032Speter if (mlp < mlist || *rvp != NULL) 129838032Speter { 129938032Speter if (tTd(21, 10)) 130090792Sgshapiro sm_dprintf("----- rule fails\n"); 130138032Speter rwr = rwr->r_next; 130238032Speter ruleno++; 130338032Speter loopcount = 0; 130438032Speter continue; 130538032Speter } 130638032Speter 130738032Speter rvp = rwr->r_rhs; 130838032Speter if (tTd(21, 12)) 130938032Speter { 131090792Sgshapiro sm_dprintf("-----rule matches:"); 1311132943Sgshapiro printav(sm_debug_file(), rvp); 131238032Speter } 131338032Speter 131438032Speter rp = *rvp; 131564562Sgshapiro if (rp != NULL) 131638032Speter { 1317168515Sgshapiro if ((rp[0] & 0377) == CANONUSER) 131864562Sgshapiro { 131964562Sgshapiro rvp++; 132064562Sgshapiro rwr = rwr->r_next; 132164562Sgshapiro ruleno++; 132264562Sgshapiro loopcount = 0; 132364562Sgshapiro } 1324168515Sgshapiro else if ((rp[0] & 0377) == CANONHOST) 132564562Sgshapiro { 132664562Sgshapiro rvp++; 132764562Sgshapiro rwr = NULL; 132864562Sgshapiro } 132938032Speter } 133038032Speter 133138032Speter /* substitute */ 133238032Speter for (avp = npvp; *rvp != NULL; rvp++) 133338032Speter { 133438032Speter register struct match *m; 133538032Speter register char **pp; 133638032Speter 133738032Speter rp = *rvp; 1338168515Sgshapiro if ((rp[0] & 0377) == MATCHREPL) 133938032Speter { 134038032Speter /* substitute from LHS */ 134138032Speter m = &mlist[rp[1] - '1']; 134238032Speter if (m < mlist || m >= mlp) 134338032Speter { 134464562Sgshapiro syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds", 134564562Sgshapiro rulename, rp[1]); 134638032Speter return EX_CONFIG; 134738032Speter } 134838032Speter if (tTd(21, 15)) 134938032Speter { 135090792Sgshapiro sm_dprintf("$%c:", rp[1]); 135164562Sgshapiro pp = m->match_first; 135264562Sgshapiro while (pp <= m->match_last) 135338032Speter { 135490792Sgshapiro sm_dprintf(" %p=\"", *pp); 135590792Sgshapiro sm_dflush(); 135690792Sgshapiro sm_dprintf("%s\"", *pp++); 135738032Speter } 135890792Sgshapiro sm_dprintf("\n"); 135938032Speter } 136064562Sgshapiro pp = m->match_first; 136164562Sgshapiro while (pp <= m->match_last) 136238032Speter { 136390792Sgshapiro if (avp >= &npvp[maxatom]) 1364120256Sgshapiro goto toolong; 136538032Speter *avp++ = *pp++; 136638032Speter } 136738032Speter } 136838032Speter else 136938032Speter { 137038032Speter /* some sort of replacement */ 137190792Sgshapiro if (avp >= &npvp[maxatom]) 137238032Speter { 137338032Speter toolong: 137464562Sgshapiro syserr("554 5.3.0 rewrite: expansion too long"); 137590792Sgshapiro if (LogLevel > 9) 137690792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 137790792Sgshapiro "rewrite: expansion too long, ruleset=%s, ruleno=%d", 137890792Sgshapiro rulename, ruleno); 137938032Speter return EX_DATAERR; 138038032Speter } 1381168515Sgshapiro if ((rp[0] & 0377) != MACRODEXPAND) 138238032Speter { 1383168515Sgshapiro /* vanilla replacement from RHS */ 138438032Speter *avp++ = rp; 138538032Speter } 138638032Speter else 138738032Speter { 138898841Sgshapiro /* $&{x} replacement */ 138938032Speter char *mval = macvalue(rp[1], e); 139038032Speter char **xpvp; 1391157001Sgshapiro size_t trsize = 0; 139238032Speter static size_t pvpb1_size = 0; 139338032Speter static char **pvpb1 = NULL; 139438032Speter char pvpbuf[PSBUFSIZE]; 139538032Speter 139638032Speter if (tTd(21, 2)) 139798841Sgshapiro sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n", 139838032Speter macname(rp[1]), 139938032Speter mval == NULL ? "(NULL)" : mval); 140038032Speter if (mval == NULL || *mval == '\0') 140138032Speter continue; 140238032Speter 140338032Speter /* save the remainder of the input */ 140438032Speter for (xpvp = pvp; *xpvp != NULL; xpvp++) 1405168515Sgshapiro trsize += sizeof(*xpvp); 1406157001Sgshapiro if (trsize > pvpb1_size) 140738032Speter { 140838032Speter if (pvpb1 != NULL) 140977349Sgshapiro sm_free(pvpb1); 141090792Sgshapiro pvpb1 = (char **) 141190792Sgshapiro sm_pmalloc_x(trsize); 141238032Speter pvpb1_size = trsize; 141338032Speter } 141438032Speter 141564562Sgshapiro memmove((char *) pvpb1, 141664562Sgshapiro (char *) pvp, 141764562Sgshapiro trsize); 141838032Speter 141938032Speter /* scan the new replacement */ 142038032Speter xpvp = prescan(mval, '\0', pvpbuf, 1421168515Sgshapiro sizeof(pvpbuf), NULL, 1422132943Sgshapiro NULL, false); 142338032Speter if (xpvp == NULL) 142438032Speter { 142538032Speter /* prescan pre-printed error */ 142638032Speter return EX_DATAERR; 142738032Speter } 142838032Speter 142938032Speter /* insert it into the output stream */ 143038032Speter while (*xpvp != NULL) 143138032Speter { 143238032Speter if (tTd(21, 19)) 143390792Sgshapiro sm_dprintf(" ... %s\n", 143464562Sgshapiro *xpvp); 143590792Sgshapiro *avp++ = sm_rpool_strdup_x( 143690792Sgshapiro e->e_rpool, *xpvp); 143790792Sgshapiro if (avp >= &npvp[maxatom]) 143838032Speter goto toolong; 143938032Speter xpvp++; 144038032Speter } 144138032Speter if (tTd(21, 19)) 144290792Sgshapiro sm_dprintf(" ... DONE\n"); 144338032Speter 144438032Speter /* restore the old trailing input */ 144564562Sgshapiro memmove((char *) pvp, 144664562Sgshapiro (char *) pvpb1, 144764562Sgshapiro trsize); 144838032Speter } 144938032Speter } 145038032Speter } 145138032Speter *avp++ = NULL; 145238032Speter 145338032Speter /* 145438032Speter ** Check for any hostname/keyword lookups. 145538032Speter */ 145638032Speter 145738032Speter for (rvp = npvp; *rvp != NULL; rvp++) 145838032Speter { 145938032Speter char **hbrvp; 146038032Speter char **xpvp; 1461157001Sgshapiro size_t trsize; 146238032Speter char *replac; 146338032Speter int endtoken; 1464182352Sgshapiro bool external; 146538032Speter STAB *map; 146638032Speter char *mapname; 146738032Speter char **key_rvp; 146838032Speter char **arg_rvp; 146938032Speter char **default_rvp; 1470157001Sgshapiro char cbuf[MAXKEY]; 147138032Speter char *pvpb1[MAXATOM + 1]; 1472120256Sgshapiro char *argvect[MAX_MAP_ARGS]; 147338032Speter char pvpbuf[PSBUFSIZE]; 147438032Speter char *nullpvp[1]; 147538032Speter 147638032Speter hbrvp = rvp; 1477168515Sgshapiro if ((rvp[0][0] & 0377) == HOSTBEGIN) 147838032Speter { 147938032Speter endtoken = HOSTEND; 148038032Speter mapname = "host"; 148138032Speter } 1482168515Sgshapiro else if ((rvp[0][0] & 0377) == LOOKUPBEGIN) 148338032Speter { 148438032Speter endtoken = LOOKUPEND; 148538032Speter mapname = *++rvp; 1486120256Sgshapiro if (mapname == NULL) 1487159609Sgshapiro { 1488120256Sgshapiro syserr("554 5.3.0 rewrite: missing mapname"); 1489159609Sgshapiro /* NOTREACHED */ 1490159609Sgshapiro SM_ASSERT(0); 1491159609Sgshapiro } 149238032Speter } 1493168515Sgshapiro else 1494168515Sgshapiro continue; 1495168515Sgshapiro 1496168515Sgshapiro /* 1497168515Sgshapiro ** Got a hostname/keyword lookup. 1498168515Sgshapiro ** 1499168515Sgshapiro ** This could be optimized fairly easily. 1500168515Sgshapiro */ 1501168515Sgshapiro 150238032Speter map = stab(mapname, ST_MAP, ST_FIND); 150338032Speter if (map == NULL) 1504120256Sgshapiro syserr("554 5.3.0 rewrite: map %s not found", 1505120256Sgshapiro mapname); 150638032Speter 150738032Speter /* extract the match part */ 150838032Speter key_rvp = ++rvp; 1509120256Sgshapiro if (key_rvp == NULL) 1510159609Sgshapiro { 1511120256Sgshapiro syserr("554 5.3.0 rewrite: missing key for map %s", 1512120256Sgshapiro mapname); 1513159609Sgshapiro /* NOTREACHED */ 1514159609Sgshapiro SM_ASSERT(0); 1515159609Sgshapiro } 151638032Speter default_rvp = NULL; 151738032Speter arg_rvp = argvect; 151838032Speter xpvp = NULL; 151938032Speter replac = pvpbuf; 1520168515Sgshapiro while (*rvp != NULL && ((rvp[0][0] & 0377) != endtoken)) 152138032Speter { 1522168515Sgshapiro int nodetype = rvp[0][0] & 0377; 152338032Speter 1524120256Sgshapiro if (nodetype != CANONHOST && 1525120256Sgshapiro nodetype != CANONUSER) 152638032Speter { 152738032Speter rvp++; 152838032Speter continue; 152938032Speter } 153038032Speter 153138032Speter *rvp++ = NULL; 153238032Speter 153338032Speter if (xpvp != NULL) 153438032Speter { 153538032Speter cataddr(xpvp, NULL, replac, 1536168515Sgshapiro &pvpbuf[sizeof(pvpbuf)] - replac, 1537168515Sgshapiro '\0', false); 1538120256Sgshapiro if (arg_rvp < 1539120256Sgshapiro &argvect[MAX_MAP_ARGS - 1]) 1540120256Sgshapiro *++arg_rvp = replac; 154138032Speter replac += strlen(replac) + 1; 154238032Speter xpvp = NULL; 154338032Speter } 154438032Speter switch (nodetype) 154538032Speter { 154638032Speter case CANONHOST: 154738032Speter xpvp = rvp; 154838032Speter break; 154938032Speter 155038032Speter case CANONUSER: 155138032Speter default_rvp = rvp; 155238032Speter break; 155338032Speter } 155438032Speter } 155538032Speter if (*rvp != NULL) 155638032Speter *rvp++ = NULL; 155738032Speter if (xpvp != NULL) 155838032Speter { 155938032Speter cataddr(xpvp, NULL, replac, 1560168515Sgshapiro &pvpbuf[sizeof(pvpbuf)] - replac, 1561168515Sgshapiro '\0', false); 1562120256Sgshapiro if (arg_rvp < &argvect[MAX_MAP_ARGS - 1]) 1563120256Sgshapiro *++arg_rvp = replac; 156438032Speter } 1565120256Sgshapiro if (arg_rvp >= &argvect[MAX_MAP_ARGS - 1]) 1566120256Sgshapiro argvect[MAX_MAP_ARGS - 1] = NULL; 1567120256Sgshapiro else 1568120256Sgshapiro *++arg_rvp = NULL; 156938032Speter 157038032Speter /* save the remainder of the input string */ 1571168515Sgshapiro trsize = (avp - rvp + 1) * sizeof(*rvp); 157264562Sgshapiro memmove((char *) pvpb1, (char *) rvp, trsize); 157338032Speter 157438032Speter /* look it up */ 1575168515Sgshapiro cataddr(key_rvp, NULL, cbuf, sizeof(cbuf), 1576168515Sgshapiro map == NULL ? '\0' : map->s_map.map_spacesub, 1577168515Sgshapiro true); 157864562Sgshapiro argvect[0] = cbuf; 157964562Sgshapiro replac = map_lookup(map, cbuf, argvect, &rstat, e); 1580182352Sgshapiro external = replac != NULL; 158138032Speter 158238032Speter /* if no replacement, use default */ 158338032Speter if (replac == NULL && default_rvp != NULL) 158438032Speter { 158538032Speter /* create the default */ 1586168515Sgshapiro cataddr(default_rvp, NULL, cbuf, sizeof(cbuf), 1587168515Sgshapiro '\0', false); 158864562Sgshapiro replac = cbuf; 158938032Speter } 159038032Speter 159138032Speter if (replac == NULL) 159238032Speter { 159338032Speter xpvp = key_rvp; 159438032Speter } 159538032Speter else if (*replac == '\0') 159638032Speter { 159738032Speter /* null replacement */ 159838032Speter nullpvp[0] = NULL; 159938032Speter xpvp = nullpvp; 160038032Speter } 160138032Speter else 160238032Speter { 160338032Speter /* scan the new replacement */ 160438032Speter xpvp = prescan(replac, '\0', pvpbuf, 1605182352Sgshapiro sizeof(pvpbuf), NULL, 1606182352Sgshapiro external ? NULL : IntTokenTab, 1607168515Sgshapiro false); 160838032Speter if (xpvp == NULL) 160938032Speter { 161038032Speter /* prescan already printed error */ 161138032Speter return EX_DATAERR; 161238032Speter } 161338032Speter } 161438032Speter 161538032Speter /* append it to the token list */ 161638032Speter for (avp = hbrvp; *xpvp != NULL; xpvp++) 161738032Speter { 161890792Sgshapiro *avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp); 161990792Sgshapiro if (avp >= &npvp[maxatom]) 162038032Speter goto toolong; 162138032Speter } 162238032Speter 162338032Speter /* restore the old trailing information */ 162438032Speter rvp = avp - 1; 162538032Speter for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 162690792Sgshapiro if (avp >= &npvp[maxatom]) 162738032Speter goto toolong; 162838032Speter } 162938032Speter 163038032Speter /* 163138032Speter ** Check for subroutine calls. 163238032Speter */ 163338032Speter 163464562Sgshapiro status = callsubr(npvp, reclevel, e); 163564562Sgshapiro if (rstat == EX_OK || status == EX_TEMPFAIL) 163664562Sgshapiro rstat = status; 163738032Speter 163838032Speter /* copy vector back into original space. */ 163938032Speter for (avp = npvp; *avp++ != NULL;) 164038032Speter continue; 164164562Sgshapiro memmove((char *) pvp, (char *) npvp, 1642168515Sgshapiro (int) (avp - npvp) * sizeof(*avp)); 164364562Sgshapiro 164438032Speter if (tTd(21, 4)) 164538032Speter { 164690792Sgshapiro sm_dprintf("rewritten as:"); 1647132943Sgshapiro printav(sm_debug_file(), pvp); 164838032Speter } 164938032Speter } 165038032Speter 165164562Sgshapiro if (OpMode == MD_TEST) 165238032Speter { 165390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 165490792Sgshapiro "%s%-16.16s returns:", prefix, rulename); 1655132943Sgshapiro printav(smioout, pvp); 165638032Speter } 165764562Sgshapiro else if (tTd(21, 1)) 165864562Sgshapiro { 165990792Sgshapiro sm_dprintf("%s%-16.16s returns:", prefix, rulename); 1660132943Sgshapiro printav(sm_debug_file(), pvp); 166164562Sgshapiro } 166238032Speter return rstat; 166338032Speter} 166490792Sgshapiro/* 166538032Speter** CALLSUBR -- call subroutines in rewrite vector 166638032Speter** 166738032Speter** Parameters: 166838032Speter** pvp -- pointer to token vector. 166938032Speter** reclevel -- the current recursion level. 167038032Speter** e -- the current envelope. 167138032Speter** 167238032Speter** Returns: 167338032Speter** The status from the subroutine call. 167438032Speter** 167538032Speter** Side Effects: 167638032Speter** pvp is modified. 167738032Speter*/ 167838032Speter 167964562Sgshapirostatic int 168038032Spetercallsubr(pvp, reclevel, e) 168138032Speter char **pvp; 168238032Speter int reclevel; 168338032Speter ENVELOPE *e; 168438032Speter{ 168564562Sgshapiro char **avp; 168638032Speter register int i; 168790792Sgshapiro int subr, j; 168890792Sgshapiro int nsubr; 168964562Sgshapiro int status; 169038032Speter int rstat = EX_OK; 169190792Sgshapiro#define MAX_SUBR 16 169290792Sgshapiro int subrnumber[MAX_SUBR]; 169390792Sgshapiro int subrindex[MAX_SUBR]; 169438032Speter 169590792Sgshapiro nsubr = 0; 169690792Sgshapiro 169790792Sgshapiro /* 169890792Sgshapiro ** Look for subroutine calls in pvp, collect them into subr*[] 169990792Sgshapiro ** We will perform the calls in the next loop, because we will 170090792Sgshapiro ** call the "last" subroutine first to avoid recursive calls 170190792Sgshapiro ** and too much copying. 170290792Sgshapiro */ 170390792Sgshapiro 170490792Sgshapiro for (avp = pvp, j = 0; *avp != NULL; avp++, j++) 170538032Speter { 1706168515Sgshapiro if ((avp[0][0] & 0377) == CALLSUBR && avp[1] != NULL) 170738032Speter { 170838032Speter stripquotes(avp[1]); 170938032Speter subr = strtorwset(avp[1], NULL, ST_FIND); 171038032Speter if (subr < 0) 171138032Speter { 171290792Sgshapiro syserr("554 5.3.5 Unknown ruleset %s", avp[1]); 171338032Speter return EX_CONFIG; 171438032Speter } 171538032Speter 171638032Speter /* 171790792Sgshapiro ** XXX instead of doing this we could optimize 171890792Sgshapiro ** the rules after reading them: just remove 171990792Sgshapiro ** calls to empty rulesets 172038032Speter */ 172138032Speter 172290792Sgshapiro /* subroutine is an empty ruleset? don't call it */ 172390792Sgshapiro if (RewriteRules[subr] == NULL) 172490792Sgshapiro { 172590792Sgshapiro if (tTd(21, 3)) 172690792Sgshapiro sm_dprintf("-----skip subr %s (%d)\n", 172790792Sgshapiro avp[1], subr); 172890792Sgshapiro for (i = 2; avp[i] != NULL; i++) 172990792Sgshapiro avp[i - 2] = avp[i]; 173090792Sgshapiro avp[i - 2] = NULL; 173138032Speter continue; 173290792Sgshapiro } 173390792Sgshapiro if (++nsubr >= MAX_SUBR) 173438032Speter { 173590792Sgshapiro syserr("554 5.3.0 Too many subroutine calls (%d max)", 173690792Sgshapiro MAX_SUBR); 173790792Sgshapiro return EX_CONFIG; 173838032Speter } 173990792Sgshapiro subrnumber[nsubr] = subr; 174090792Sgshapiro subrindex[nsubr] = j; 174190792Sgshapiro } 174290792Sgshapiro } 174338032Speter 174490792Sgshapiro /* 174590792Sgshapiro ** Perform the actual subroutines calls, "last" one first, i.e., 174690792Sgshapiro ** go from the right to the left through all calls, 174790792Sgshapiro ** do the rewriting in place. 174890792Sgshapiro */ 174938032Speter 175090792Sgshapiro for (; nsubr > 0; nsubr--) 175190792Sgshapiro { 175290792Sgshapiro subr = subrnumber[nsubr]; 175390792Sgshapiro avp = pvp + subrindex[nsubr]; 175438032Speter 175590792Sgshapiro /* remove the subroutine call and name */ 175690792Sgshapiro for (i = 2; avp[i] != NULL; i++) 175790792Sgshapiro avp[i - 2] = avp[i]; 175890792Sgshapiro avp[i - 2] = NULL; 175938032Speter 176090792Sgshapiro /* 176190792Sgshapiro ** Now we need to call the ruleset specified for 1762120256Sgshapiro ** the subroutine. We can do this in place since 176390792Sgshapiro ** we call the "last" subroutine first. 176490792Sgshapiro */ 176590792Sgshapiro 176690792Sgshapiro status = rewrite(avp, subr, reclevel, e, 176790792Sgshapiro MAXATOM - subrindex[nsubr]); 176890792Sgshapiro if (status != EX_OK && status != EX_TEMPFAIL) 176990792Sgshapiro return status; 177090792Sgshapiro if (rstat == EX_OK || status == EX_TEMPFAIL) 177190792Sgshapiro rstat = status; 177238032Speter } 177338032Speter return rstat; 177438032Speter} 177590792Sgshapiro/* 177638032Speter** MAP_LOOKUP -- do lookup in map 177738032Speter** 177838032Speter** Parameters: 177990792Sgshapiro** smap -- the map to use for the lookup. 178038032Speter** key -- the key to look up. 178138032Speter** argvect -- arguments to pass to the map lookup. 178238032Speter** pstat -- a pointer to an integer in which to store the 178338032Speter** status from the lookup. 178438032Speter** e -- the current envelope. 178538032Speter** 178638032Speter** Returns: 178738032Speter** The result of the lookup. 178838032Speter** NULL -- if there was no data for the given key. 178938032Speter*/ 179038032Speter 179164562Sgshapirostatic char * 179264562Sgshapiromap_lookup(smap, key, argvect, pstat, e) 179364562Sgshapiro STAB *smap; 179438032Speter char key[]; 179538032Speter char **argvect; 179638032Speter int *pstat; 179738032Speter ENVELOPE *e; 179838032Speter{ 179964562Sgshapiro auto int status = EX_OK; 180064562Sgshapiro MAP *map; 180138032Speter char *replac; 180238032Speter 180364562Sgshapiro if (smap == NULL) 180464562Sgshapiro return NULL; 180564562Sgshapiro 180664562Sgshapiro map = &smap->s_map; 180764562Sgshapiro DYNOPENMAP(map); 180864562Sgshapiro 180964562Sgshapiro if (e->e_sendmode == SM_DEFER && 181064562Sgshapiro bitset(MF_DEFER, map->map_mflags)) 181138032Speter { 181238032Speter /* don't do any map lookups */ 181338032Speter if (tTd(60, 1)) 181490792Sgshapiro sm_dprintf("map_lookup(%s, %s) => DEFERRED\n", 181564562Sgshapiro smap->s_name, key); 181638032Speter *pstat = EX_TEMPFAIL; 181738032Speter return NULL; 181838032Speter } 181938032Speter 182064562Sgshapiro if (!bitset(MF_KEEPQUOTES, map->map_mflags)) 182138032Speter stripquotes(key); 182238032Speter 182338032Speter if (tTd(60, 1)) 182442575Speter { 1825168515Sgshapiro sm_dprintf("map_lookup(%s, ", smap->s_name); 1826168515Sgshapiro xputs(sm_debug_file(), key); 182742575Speter if (tTd(60, 5)) 182842575Speter { 182942575Speter int i; 183042575Speter 183142575Speter for (i = 0; argvect[i] != NULL; i++) 183290792Sgshapiro sm_dprintf(", %%%d=%s", i, argvect[i]); 183342575Speter } 183490792Sgshapiro sm_dprintf(") => "); 183542575Speter } 183664562Sgshapiro replac = (*map->map_class->map_lookup)(map, key, argvect, &status); 183738032Speter if (tTd(60, 1)) 183890792Sgshapiro sm_dprintf("%s (%d)\n", 183938032Speter replac != NULL ? replac : "NOT FOUND", 184064562Sgshapiro status); 184138032Speter 184264562Sgshapiro /* should recover if status == EX_TEMPFAIL */ 184364562Sgshapiro if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags)) 184438032Speter { 184538032Speter *pstat = EX_TEMPFAIL; 184638032Speter if (tTd(60, 1)) 184790792Sgshapiro sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n", 184864562Sgshapiro smap->s_name, key, errno); 184938032Speter if (e->e_message == NULL) 185038032Speter { 185138032Speter char mbuf[320]; 185238032Speter 1853168515Sgshapiro (void) sm_snprintf(mbuf, sizeof(mbuf), 185438032Speter "%.80s map: lookup (%s): deferred", 185564562Sgshapiro smap->s_name, 185638032Speter shortenstring(key, MAXSHORTSTR)); 185790792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf); 185838032Speter } 185938032Speter } 186064562Sgshapiro if (status == EX_TEMPFAIL && map->map_tapp != NULL) 186138032Speter { 186264562Sgshapiro size_t i = strlen(key) + strlen(map->map_tapp) + 1; 186338032Speter static char *rwbuf = NULL; 186438032Speter static size_t rwbuflen = 0; 186538032Speter 186638032Speter if (i > rwbuflen) 186738032Speter { 186838032Speter if (rwbuf != NULL) 186977349Sgshapiro sm_free(rwbuf); 187038032Speter rwbuflen = i; 187190792Sgshapiro rwbuf = (char *) sm_pmalloc_x(rwbuflen); 187238032Speter } 187390792Sgshapiro (void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp); 187438032Speter if (tTd(60, 4)) 187590792Sgshapiro sm_dprintf("map_lookup tempfail: returning \"%s\"\n", 187638032Speter rwbuf); 187738032Speter return rwbuf; 187838032Speter } 187938032Speter return replac; 188038032Speter} 188190792Sgshapiro/* 188264562Sgshapiro** INITERRMAILERS -- initialize error and discard mailers 188364562Sgshapiro** 188464562Sgshapiro** Parameters: 188564562Sgshapiro** none. 188664562Sgshapiro** 188764562Sgshapiro** Returns: 188864562Sgshapiro** none. 188964562Sgshapiro** 189064562Sgshapiro** Side Effects: 189164562Sgshapiro** initializes error and discard mailers. 189264562Sgshapiro*/ 189364562Sgshapiro 189464562Sgshapirostatic MAILER discardmailer; 189564562Sgshapirostatic MAILER errormailer; 189664562Sgshapirostatic char *discardargv[] = { "DISCARD", NULL }; 189764562Sgshapirostatic char *errorargv[] = { "ERROR", NULL }; 189864562Sgshapiro 189964562Sgshapirovoid 190064562Sgshapiroiniterrmailers() 190164562Sgshapiro{ 190264562Sgshapiro if (discardmailer.m_name == NULL) 190364562Sgshapiro { 190464562Sgshapiro /* initialize the discard mailer */ 190564562Sgshapiro discardmailer.m_name = "*discard*"; 190664562Sgshapiro discardmailer.m_mailer = "DISCARD"; 190764562Sgshapiro discardmailer.m_argv = discardargv; 190864562Sgshapiro } 190964562Sgshapiro if (errormailer.m_name == NULL) 191064562Sgshapiro { 191164562Sgshapiro /* initialize the bogus mailer */ 191264562Sgshapiro errormailer.m_name = "*error*"; 191364562Sgshapiro errormailer.m_mailer = "ERROR"; 191464562Sgshapiro errormailer.m_argv = errorargv; 191564562Sgshapiro } 191664562Sgshapiro} 191790792Sgshapiro/* 191838032Speter** BUILDADDR -- build address from token vector. 191938032Speter** 192038032Speter** Parameters: 192138032Speter** tv -- token vector. 192238032Speter** a -- pointer to address descriptor to fill. 192338032Speter** If NULL, one will be allocated. 192438032Speter** flags -- info regarding whether this is a sender or 192538032Speter** a recipient. 192638032Speter** e -- the current envelope. 192738032Speter** 192838032Speter** Returns: 192938032Speter** NULL if there was an error. 193038032Speter** 'a' otherwise. 193138032Speter** 193238032Speter** Side Effects: 193338032Speter** fills in 'a' 193438032Speter*/ 193538032Speter 193664562Sgshapirostatic struct errcodes 193738032Speter{ 193838032Speter char *ec_name; /* name of error code */ 193938032Speter int ec_code; /* numeric code */ 194038032Speter} ErrorCodes[] = 194138032Speter{ 194238032Speter { "usage", EX_USAGE }, 194338032Speter { "nouser", EX_NOUSER }, 194438032Speter { "nohost", EX_NOHOST }, 194538032Speter { "unavailable", EX_UNAVAILABLE }, 194638032Speter { "software", EX_SOFTWARE }, 194738032Speter { "tempfail", EX_TEMPFAIL }, 194838032Speter { "protocol", EX_PROTOCOL }, 194938032Speter { "config", EX_CONFIG }, 195038032Speter { NULL, EX_UNAVAILABLE } 195138032Speter}; 195238032Speter 195364562Sgshapirostatic ADDRESS * 195438032Speterbuildaddr(tv, a, flags, e) 195538032Speter register char **tv; 195638032Speter register ADDRESS *a; 195738032Speter int flags; 195838032Speter register ENVELOPE *e; 195938032Speter{ 196094334Sgshapiro bool tempfail = false; 1961120256Sgshapiro int maxatom; 196238032Speter struct mailer **mp; 196338032Speter register struct mailer *m; 196438032Speter register char *p; 196538032Speter char *mname; 196638032Speter char **hostp; 196738032Speter char hbuf[MAXNAME + 1]; 196842575Speter static char ubuf[MAXNAME + 2]; 196938032Speter 197038032Speter if (tTd(24, 5)) 197138032Speter { 197290792Sgshapiro sm_dprintf("buildaddr, flags=%x, tv=", flags); 1973132943Sgshapiro printav(sm_debug_file(), tv); 197438032Speter } 197538032Speter 1976120256Sgshapiro maxatom = MAXATOM; 197738032Speter if (a == NULL) 1978168515Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a)); 1979168515Sgshapiro memset((char *) a, '\0', sizeof(*a)); 198064562Sgshapiro hbuf[0] = '\0'; 198138032Speter 198238032Speter /* set up default error return flags */ 198338032Speter a->q_flags |= DefaultNotify; 198438032Speter 198538032Speter /* figure out what net/mailer to use */ 198638032Speter if (*tv == NULL || (**tv & 0377) != CANONNET) 198738032Speter { 198864562Sgshapiro syserr("554 5.3.5 buildaddr: no mailer in parsed address"); 198938032Speterbadaddr: 199094334Sgshapiro /* 199194334Sgshapiro ** ExitStat may have been set by an earlier map open 199294334Sgshapiro ** failure (to a permanent error (EX_OSERR) in syserr()) 199394334Sgshapiro ** so we also need to check if this particular $#error 199494334Sgshapiro ** return wanted a 4XX failure. 199594334Sgshapiro ** 199694334Sgshapiro ** XXX the real fix is probably to set ExitStat correctly, 199794334Sgshapiro ** i.e., to EX_TEMPFAIL if the map open is just a temporary 199894334Sgshapiro ** error. 199994334Sgshapiro */ 200094334Sgshapiro 200194334Sgshapiro if (ExitStat == EX_TEMPFAIL || tempfail) 200264562Sgshapiro a->q_state = QS_QUEUEUP; 200364562Sgshapiro else 200438032Speter { 200564562Sgshapiro a->q_state = QS_BADADDR; 200664562Sgshapiro a->q_mailer = &errormailer; 200738032Speter } 200838032Speter return a; 200938032Speter } 201038032Speter mname = *++tv; 2011120256Sgshapiro --maxatom; 201238032Speter 201338032Speter /* extract host and user portions */ 201438032Speter if (*++tv != NULL && (**tv & 0377) == CANONHOST) 2015120256Sgshapiro { 201638032Speter hostp = ++tv; 2017120256Sgshapiro --maxatom; 2018120256Sgshapiro } 201938032Speter else 202038032Speter hostp = NULL; 2021120256Sgshapiro --maxatom; 202238032Speter while (*tv != NULL && (**tv & 0377) != CANONUSER) 2023120256Sgshapiro { 202438032Speter tv++; 2025120256Sgshapiro --maxatom; 2026120256Sgshapiro } 202738032Speter if (*tv == NULL) 202838032Speter { 202964562Sgshapiro syserr("554 5.3.5 buildaddr: no user"); 203038032Speter goto badaddr; 203138032Speter } 203238032Speter if (tv == hostp) 203338032Speter hostp = NULL; 203438032Speter else if (hostp != NULL) 2035168515Sgshapiro cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', false); 2036168515Sgshapiro cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false); 2037120256Sgshapiro --maxatom; 203838032Speter 203938032Speter /* save away the host name */ 204090792Sgshapiro if (sm_strcasecmp(mname, "error") == 0) 204138032Speter { 204264562Sgshapiro /* Set up triplet for use by -bv */ 204364562Sgshapiro a->q_mailer = &errormailer; 204490792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); 204590792Sgshapiro /* XXX wrong place? */ 204664562Sgshapiro 204738032Speter if (hostp != NULL) 204838032Speter { 204938032Speter register struct errcodes *ep; 205038032Speter 205190792Sgshapiro a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); 205238032Speter if (strchr(hbuf, '.') != NULL) 205338032Speter { 205490792Sgshapiro a->q_status = sm_rpool_strdup_x(e->e_rpool, 205590792Sgshapiro hbuf); 205638032Speter setstat(dsntoexitstat(hbuf)); 205738032Speter } 205838032Speter else if (isascii(hbuf[0]) && isdigit(hbuf[0])) 205938032Speter { 206038032Speter setstat(atoi(hbuf)); 206138032Speter } 206238032Speter else 206338032Speter { 206438032Speter for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 206590792Sgshapiro if (sm_strcasecmp(ep->ec_name, hbuf) == 0) 206638032Speter break; 206738032Speter setstat(ep->ec_code); 206838032Speter } 206938032Speter } 207038032Speter else 207164562Sgshapiro { 207264562Sgshapiro a->q_host = NULL; 207338032Speter setstat(EX_UNAVAILABLE); 207464562Sgshapiro } 207538032Speter stripquotes(ubuf); 207664562Sgshapiro if (ISSMTPCODE(ubuf) && ubuf[3] == ' ') 207738032Speter { 207864562Sgshapiro char fmt[16]; 207964562Sgshapiro int off; 208038032Speter 208164562Sgshapiro if ((off = isenhsc(ubuf + 4, ' ')) > 0) 208238032Speter { 208364562Sgshapiro ubuf[off + 4] = '\0'; 208464562Sgshapiro off += 5; 208538032Speter } 208664562Sgshapiro else 208764562Sgshapiro { 208864562Sgshapiro off = 4; 208964562Sgshapiro ubuf[3] = '\0'; 209064562Sgshapiro } 2091168515Sgshapiro (void) sm_strlcpyn(fmt, sizeof(fmt), 2, ubuf, " %s"); 209264562Sgshapiro if (off > 4) 209364562Sgshapiro usrerr(fmt, ubuf + off); 209464562Sgshapiro else if (isenhsc(hbuf, '\0') > 0) 209564562Sgshapiro usrerrenh(hbuf, fmt, ubuf + off); 209664562Sgshapiro else 209764562Sgshapiro usrerr(fmt, ubuf + off); 209864562Sgshapiro /* XXX ubuf[off - 1] = ' '; */ 209994334Sgshapiro if (ubuf[0] == '4') 210094334Sgshapiro tempfail = true; 210138032Speter } 210238032Speter else 210338032Speter { 210464562Sgshapiro usrerr("553 5.3.0 %s", ubuf); 210538032Speter } 210638032Speter goto badaddr; 210738032Speter } 210838032Speter 210938032Speter for (mp = Mailer; (m = *mp++) != NULL; ) 211038032Speter { 211190792Sgshapiro if (sm_strcasecmp(m->m_name, mname) == 0) 211238032Speter break; 211338032Speter } 211438032Speter if (m == NULL) 211538032Speter { 211664562Sgshapiro syserr("554 5.3.5 buildaddr: unknown mailer %s", mname); 211738032Speter goto badaddr; 211838032Speter } 211938032Speter a->q_mailer = m; 212038032Speter 212138032Speter /* figure out what host (if any) */ 212238032Speter if (hostp == NULL) 212338032Speter { 212438032Speter if (!bitnset(M_LOCALMAILER, m->m_flags)) 212538032Speter { 212664562Sgshapiro syserr("554 5.3.5 buildaddr: no host"); 212738032Speter goto badaddr; 212838032Speter } 212938032Speter a->q_host = NULL; 213038032Speter } 213138032Speter else 213290792Sgshapiro a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); 213338032Speter 213438032Speter /* figure out the user */ 213538032Speter p = ubuf; 213638032Speter if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@') 213738032Speter { 213838032Speter p++; 213938032Speter tv++; 2140120256Sgshapiro --maxatom; 214138032Speter a->q_flags |= QNOTREMOTE; 214238032Speter } 214338032Speter 214438032Speter /* do special mapping for local mailer */ 214538032Speter if (*p == '"') 214638032Speter p++; 214738032Speter if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags)) 214838032Speter a->q_mailer = m = ProgMailer; 214938032Speter else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags)) 215038032Speter a->q_mailer = m = FileMailer; 215138032Speter else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags)) 215238032Speter { 215338032Speter /* may be :include: */ 215438032Speter stripquotes(ubuf); 215590792Sgshapiro if (sm_strncasecmp(ubuf, ":include:", 9) == 0) 215638032Speter { 215738032Speter /* if :include:, don't need further rewriting */ 215838032Speter a->q_mailer = m = InclMailer; 215990792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]); 216038032Speter return a; 216138032Speter } 216238032Speter } 216338032Speter 216438032Speter /* rewrite according recipient mailer rewriting rules */ 216590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', a->q_host); 216664562Sgshapiro 216790792Sgshapiro if (ConfigLevel >= 10 || 216864562Sgshapiro !bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) 216938032Speter { 217038032Speter /* sender addresses done later */ 2171120256Sgshapiro (void) rewrite(tv, 2, 0, e, maxatom); 217238032Speter if (m->m_re_rwset > 0) 2173120256Sgshapiro (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom); 217438032Speter } 2175120256Sgshapiro (void) rewrite(tv, 4, 0, e, maxatom); 217638032Speter 217738032Speter /* save the result for the command line/RCPT argument */ 2178168515Sgshapiro cataddr(tv, NULL, ubuf, sizeof(ubuf), '\0', true); 217990792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); 218038032Speter 218138032Speter /* 218238032Speter ** Do mapping to lower case as requested by mailer 218338032Speter */ 218438032Speter 218538032Speter if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags)) 218638032Speter makelower(a->q_host); 218738032Speter if (!bitnset(M_USR_UPPER, m->m_flags)) 218838032Speter makelower(a->q_user); 218938032Speter 219038032Speter if (tTd(24, 6)) 219138032Speter { 219290792Sgshapiro sm_dprintf("buildaddr => "); 2193132943Sgshapiro printaddr(sm_debug_file(), a, false); 219438032Speter } 219538032Speter return a; 219638032Speter} 2197110560Sgshapiro 219890792Sgshapiro/* 219938032Speter** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 220038032Speter** 220138032Speter** Parameters: 220238032Speter** pvp -- parameter vector to rebuild. 220338032Speter** evp -- last parameter to include. Can be NULL to 220438032Speter** use entire pvp. 220538032Speter** buf -- buffer to build the string into. 220638032Speter** sz -- size of buf. 220790792Sgshapiro** spacesub -- the space separator character; if '\0', 220838032Speter** use SpaceSub. 2209168515Sgshapiro** external -- convert to external form? 2210168515Sgshapiro** (no metacharacters; METAQUOTEs removed, see below) 221138032Speter** 221238032Speter** Returns: 221338032Speter** none. 221438032Speter** 221538032Speter** Side Effects: 221638032Speter** Destroys buf. 2217168515Sgshapiro** 2218168515Sgshapiro** Notes: 2219168515Sgshapiro** There are two formats for strings: internal and external. 2220168515Sgshapiro** The external format is just an eight-bit clean string (no 2221168515Sgshapiro** null bytes, everything else OK). The internal format can 2222168515Sgshapiro** include sendmail metacharacters. The special character 2223168515Sgshapiro** METAQUOTE essentially quotes the character following, stripping 2224168515Sgshapiro** it of all special semantics. 2225168515Sgshapiro** 2226168515Sgshapiro** The cataddr routine needs to be aware of whether it is producing 2227168515Sgshapiro** an internal or external form as output (it only takes internal 2228168515Sgshapiro** form as input). 2229168515Sgshapiro** 2230168515Sgshapiro** The parseaddr routine has a similar issue on input, but that 2231168515Sgshapiro** is flagged on the basis of which token table is passed in. 223238032Speter*/ 223338032Speter 223438032Spetervoid 2235168515Sgshapirocataddr(pvp, evp, buf, sz, spacesub, external) 223638032Speter char **pvp; 223738032Speter char **evp; 223838032Speter char *buf; 223938032Speter register int sz; 224038032Speter int spacesub; 2241168515Sgshapiro bool external; 224238032Speter{ 2243168515Sgshapiro bool oatomtok, natomtok; 2244168515Sgshapiro char *p; 224538032Speter 2246168515Sgshapiro oatomtok = natomtok = false; 2247168515Sgshapiro if (tTd(59, 14)) 2248168515Sgshapiro { 2249168515Sgshapiro sm_dprintf("cataddr(%d) <==", external); 2250168515Sgshapiro printav(sm_debug_file(), pvp); 2251168515Sgshapiro } 2252168515Sgshapiro 225364562Sgshapiro if (sz <= 0) 225464562Sgshapiro return; 225564562Sgshapiro 225638032Speter if (spacesub == '\0') 225738032Speter spacesub = SpaceSub; 225838032Speter 225938032Speter if (pvp == NULL) 226038032Speter { 226164562Sgshapiro *buf = '\0'; 226238032Speter return; 226338032Speter } 226438032Speter p = buf; 226538032Speter sz -= 2; 226690792Sgshapiro while (*pvp != NULL && sz > 0) 226738032Speter { 2268168515Sgshapiro char *q; 2269168515Sgshapiro 2270182352Sgshapiro natomtok = (IntTokenTab[**pvp & 0xff] == ATM); 227138032Speter if (oatomtok && natomtok) 227264562Sgshapiro { 227338032Speter *p++ = spacesub; 227490792Sgshapiro if (--sz <= 0) 227590792Sgshapiro break; 227664562Sgshapiro } 2277168515Sgshapiro for (q = *pvp; *q != '\0'; ) 2278168515Sgshapiro { 2279168515Sgshapiro int c; 2280168515Sgshapiro 2281168515Sgshapiro if (--sz <= 0) 2282168515Sgshapiro break; 2283168515Sgshapiro *p++ = c = *q++; 2284168515Sgshapiro 2285168515Sgshapiro /* 2286168515Sgshapiro ** If the current character (c) is METAQUOTE and we 2287168515Sgshapiro ** want the "external" form and the next character 2288168515Sgshapiro ** is not NUL, then overwrite METAQUOTE with that 2289168515Sgshapiro ** character (i.e., METAQUOTE ch is changed to 2290168515Sgshapiro ** ch). p[-1] is used because p is advanced (above). 2291168515Sgshapiro */ 2292168515Sgshapiro 2293168515Sgshapiro if ((c & 0377) == METAQUOTE && external && *q != '\0') 2294168515Sgshapiro p[-1] = *q++; 2295168515Sgshapiro } 2296132943Sgshapiro if (sz <= 0) 229790792Sgshapiro break; 229838032Speter oatomtok = natomtok; 229938032Speter if (pvp++ == evp) 230038032Speter break; 230138032Speter } 2302132943Sgshapiro 2303147078Sgshapiro#if 0 2304147078Sgshapiro /* 2305147078Sgshapiro ** Silently truncate long strings: even though this doesn't 2306147078Sgshapiro ** seem like a good idea it is necessary because header checks 2307147078Sgshapiro ** send the whole header value to rscheck() and hence rewrite(). 2308147078Sgshapiro ** The latter however sometimes uses a "short" buffer (e.g., 2309147078Sgshapiro ** cbuf[MAXNAME + 1]) to call cataddr() which then triggers this 2310147078Sgshapiro ** error function. One possible fix to the problem is to pass 2311147078Sgshapiro ** flags to rscheck() and rewrite() to distinguish the various 2312147078Sgshapiro ** calls and only trigger the error if necessary. For now just 2313147078Sgshapiro ** undo the change from 8.13.0. 2314147078Sgshapiro */ 2315147078Sgshapiro 2316132943Sgshapiro if (sz <= 0) 2317141858Sgshapiro usrerr("cataddr: string too long"); 2318147078Sgshapiro#endif 231938032Speter *p = '\0'; 2320168515Sgshapiro 2321168515Sgshapiro if (tTd(59, 14)) 2322168515Sgshapiro sm_dprintf(" cataddr => %s\n", str2prt(buf)); 232338032Speter} 2324168515Sgshapiro 232590792Sgshapiro/* 232638032Speter** SAMEADDR -- Determine if two addresses are the same 232738032Speter** 232838032Speter** This is not just a straight comparison -- if the mailer doesn't 232938032Speter** care about the host we just ignore it, etc. 233038032Speter** 233138032Speter** Parameters: 233238032Speter** a, b -- pointers to the internal forms to compare. 233338032Speter** 233438032Speter** Returns: 233590792Sgshapiro** true -- they represent the same mailbox. 233690792Sgshapiro** false -- they don't. 233738032Speter** 233838032Speter** Side Effects: 233938032Speter** none. 234038032Speter*/ 234138032Speter 234238032Speterbool 234338032Spetersameaddr(a, b) 234438032Speter register ADDRESS *a; 234538032Speter register ADDRESS *b; 234638032Speter{ 234738032Speter register ADDRESS *ca, *cb; 234838032Speter 234938032Speter /* if they don't have the same mailer, forget it */ 235038032Speter if (a->q_mailer != b->q_mailer) 235190792Sgshapiro return false; 235238032Speter 2353249729Sgshapiro /* 2354249729Sgshapiro ** Addresses resolving to error mailer 2355249729Sgshapiro ** should not be considered identical 2356249729Sgshapiro */ 2357249729Sgshapiro 2358249729Sgshapiro if (a->q_mailer == &errormailer) 2359249729Sgshapiro return false; 2360249729Sgshapiro 236138032Speter /* if the user isn't the same, we can drop out */ 236238032Speter if (strcmp(a->q_user, b->q_user) != 0) 236390792Sgshapiro return false; 236438032Speter 236538032Speter /* if we have good uids for both but they differ, these are different */ 236638032Speter if (a->q_mailer == ProgMailer) 236738032Speter { 236838032Speter ca = getctladdr(a); 236938032Speter cb = getctladdr(b); 237038032Speter if (ca != NULL && cb != NULL && 237138032Speter bitset(QGOODUID, ca->q_flags & cb->q_flags) && 237238032Speter ca->q_uid != cb->q_uid) 237390792Sgshapiro return false; 237438032Speter } 237538032Speter 237638032Speter /* otherwise compare hosts (but be careful for NULL ptrs) */ 237738032Speter if (a->q_host == b->q_host) 237838032Speter { 237938032Speter /* probably both null pointers */ 238090792Sgshapiro return true; 238138032Speter } 238238032Speter if (a->q_host == NULL || b->q_host == NULL) 238338032Speter { 238438032Speter /* only one is a null pointer */ 238590792Sgshapiro return false; 238638032Speter } 238738032Speter if (strcmp(a->q_host, b->q_host) != 0) 238890792Sgshapiro return false; 238938032Speter 239090792Sgshapiro return true; 239138032Speter} 239290792Sgshapiro/* 239338032Speter** PRINTADDR -- print address (for debugging) 239438032Speter** 239538032Speter** Parameters: 239638032Speter** a -- the address to print 239738032Speter** follow -- follow the q_next chain. 239838032Speter** 239938032Speter** Returns: 240038032Speter** none. 240138032Speter** 240238032Speter** Side Effects: 240338032Speter** none. 240438032Speter*/ 240538032Speter 240638032Speterstruct qflags 240738032Speter{ 240890792Sgshapiro char *qf_name; 240990792Sgshapiro unsigned long qf_bit; 241038032Speter}; 241138032Speter 241264562Sgshapirostatic struct qflags AddressFlags[] = 241338032Speter{ 241438032Speter { "QGOODUID", QGOODUID }, 241538032Speter { "QPRIMARY", QPRIMARY }, 241638032Speter { "QNOTREMOTE", QNOTREMOTE }, 241738032Speter { "QSELFREF", QSELFREF }, 241838032Speter { "QBOGUSSHELL", QBOGUSSHELL }, 241938032Speter { "QUNSAFEADDR", QUNSAFEADDR }, 242038032Speter { "QPINGONSUCCESS", QPINGONSUCCESS }, 242138032Speter { "QPINGONFAILURE", QPINGONFAILURE }, 242238032Speter { "QPINGONDELAY", QPINGONDELAY }, 242338032Speter { "QHASNOTIFY", QHASNOTIFY }, 242438032Speter { "QRELAYED", QRELAYED }, 242538032Speter { "QEXPANDED", QEXPANDED }, 242638032Speter { "QDELIVERED", QDELIVERED }, 242738032Speter { "QDELAYED", QDELAYED }, 242838032Speter { "QTHISPASS", QTHISPASS }, 242938032Speter { "QRCPTOK", QRCPTOK }, 243071345Sgshapiro { NULL, 0 } 243138032Speter}; 243238032Speter 243338032Spetervoid 2434132943Sgshapiroprintaddr(fp, a, follow) 2435132943Sgshapiro SM_FILE_T *fp; 243638032Speter register ADDRESS *a; 243738032Speter bool follow; 243838032Speter{ 243938032Speter register MAILER *m; 244038032Speter MAILER pseudomailer; 244138032Speter register struct qflags *qfp; 244238032Speter bool firstone; 244338032Speter 244438032Speter if (a == NULL) 244538032Speter { 2446132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n"); 244738032Speter return; 244838032Speter } 244938032Speter 245038032Speter while (a != NULL) 245138032Speter { 2452132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", a); 2453132943Sgshapiro (void) sm_io_flush(fp, SM_TIME_DEFAULT); 245438032Speter 245538032Speter /* find the mailer -- carefully */ 245638032Speter m = a->q_mailer; 245738032Speter if (m == NULL) 245838032Speter { 245938032Speter m = &pseudomailer; 246038032Speter m->m_mno = -1; 246138032Speter m->m_name = "NULL"; 246238032Speter } 246338032Speter 2464132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 246590792Sgshapiro "%s:\n\tmailer %d (%s), host `%s'\n", 246690792Sgshapiro a->q_paddr == NULL ? "<null>" : a->q_paddr, 246790792Sgshapiro m->m_mno, m->m_name, 246890792Sgshapiro a->q_host == NULL ? "<null>" : a->q_host); 2469132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 247090792Sgshapiro "\tuser `%s', ruser `%s'\n", 247190792Sgshapiro a->q_user, 247290792Sgshapiro a->q_ruser == NULL ? "<null>" : a->q_ruser); 2473132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate="); 247464562Sgshapiro switch (a->q_state) 247564562Sgshapiro { 247664562Sgshapiro case QS_OK: 2477132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK"); 247864562Sgshapiro break; 247964562Sgshapiro 248064562Sgshapiro case QS_DONTSEND: 2481132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 248290792Sgshapiro "DONTSEND"); 248364562Sgshapiro break; 248464562Sgshapiro 248564562Sgshapiro case QS_BADADDR: 2486132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 248790792Sgshapiro "BADADDR"); 248864562Sgshapiro break; 248964562Sgshapiro 249064562Sgshapiro case QS_QUEUEUP: 2491132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 249290792Sgshapiro "QUEUEUP"); 249364562Sgshapiro break; 249464562Sgshapiro 249590792Sgshapiro case QS_RETRY: 2496132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY"); 249790792Sgshapiro break; 249890792Sgshapiro 249964562Sgshapiro case QS_SENT: 2500132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT"); 250164562Sgshapiro break; 250264562Sgshapiro 250364562Sgshapiro case QS_VERIFIED: 2504132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 250590792Sgshapiro "VERIFIED"); 250664562Sgshapiro break; 250764562Sgshapiro 250864562Sgshapiro case QS_EXPANDED: 2509132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 251090792Sgshapiro "EXPANDED"); 251164562Sgshapiro break; 251264562Sgshapiro 251364562Sgshapiro case QS_SENDER: 2514132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 251590792Sgshapiro "SENDER"); 251664562Sgshapiro break; 251764562Sgshapiro 251864562Sgshapiro case QS_CLONED: 2519132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 252090792Sgshapiro "CLONED"); 252164562Sgshapiro break; 252264562Sgshapiro 252364562Sgshapiro case QS_DISCARDED: 2524132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 252590792Sgshapiro "DISCARDED"); 252664562Sgshapiro break; 252764562Sgshapiro 252864562Sgshapiro case QS_REPLACED: 2529132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 253090792Sgshapiro "REPLACED"); 253164562Sgshapiro break; 253264562Sgshapiro 253364562Sgshapiro case QS_REMOVED: 2534132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 253590792Sgshapiro "REMOVED"); 253664562Sgshapiro break; 253764562Sgshapiro 253864562Sgshapiro case QS_DUPLICATE: 2539132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 254090792Sgshapiro "DUPLICATE"); 254164562Sgshapiro break; 254264562Sgshapiro 254364562Sgshapiro case QS_INCLUDED: 2544132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 254590792Sgshapiro "INCLUDED"); 254664562Sgshapiro break; 254764562Sgshapiro 254864562Sgshapiro default: 2549132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 255090792Sgshapiro "%d", a->q_state); 255164562Sgshapiro break; 255264562Sgshapiro } 2553132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 255490792Sgshapiro ", next=%p, alias %p, uid %d, gid %d\n", 255590792Sgshapiro a->q_next, a->q_alias, 255690792Sgshapiro (int) a->q_uid, (int) a->q_gid); 2557132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<", 255890792Sgshapiro a->q_flags); 255990792Sgshapiro firstone = true; 256038032Speter for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) 256138032Speter { 256238032Speter if (!bitset(qfp->qf_bit, a->q_flags)) 256338032Speter continue; 256438032Speter if (!firstone) 2565132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 256690792Sgshapiro ","); 256790792Sgshapiro firstone = false; 2568132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 256990792Sgshapiro qfp->qf_name); 257038032Speter } 2571132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n"); 2572132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 257390792Sgshapiro "\towner=%s, home=\"%s\", fullname=\"%s\"\n", 257490792Sgshapiro a->q_owner == NULL ? "(none)" : a->q_owner, 257590792Sgshapiro a->q_home == NULL ? "(none)" : a->q_home, 257690792Sgshapiro a->q_fullname == NULL ? "(none)" : a->q_fullname); 2577132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 257890792Sgshapiro "\torcpt=\"%s\", statmta=%s, status=%s\n", 257990792Sgshapiro a->q_orcpt == NULL ? "(none)" : a->q_orcpt, 258090792Sgshapiro a->q_statmta == NULL ? "(none)" : a->q_statmta, 258190792Sgshapiro a->q_status == NULL ? "(none)" : a->q_status); 2582132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 258390792Sgshapiro "\tfinalrcpt=\"%s\"\n", 258490792Sgshapiro a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt); 2585132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 258690792Sgshapiro "\trstatus=\"%s\"\n", 258790792Sgshapiro a->q_rstatus == NULL ? "(none)" : a->q_rstatus); 2588132943Sgshapiro (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 258990792Sgshapiro "\tstatdate=%s\n", 259090792Sgshapiro a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate)); 259138032Speter 259238032Speter if (!follow) 259338032Speter return; 259438032Speter a = a->q_next; 259538032Speter } 259638032Speter} 259790792Sgshapiro/* 259890792Sgshapiro** EMPTYADDR -- return true if this address is empty (``<>'') 259938032Speter** 260038032Speter** Parameters: 260138032Speter** a -- pointer to the address 260238032Speter** 260338032Speter** Returns: 260490792Sgshapiro** true -- if this address is "empty" (i.e., no one should 260538032Speter** ever generate replies to it. 260690792Sgshapiro** false -- if it is a "regular" (read: replyable) address. 260738032Speter*/ 260838032Speter 260938032Speterbool 261038032Speteremptyaddr(a) 261138032Speter register ADDRESS *a; 261238032Speter{ 261338032Speter return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 || 261438032Speter a->q_user == NULL || strcmp(a->q_user, "<>") == 0; 261538032Speter} 261690792Sgshapiro/* 261738032Speter** REMOTENAME -- return the name relative to the current mailer 261838032Speter** 261938032Speter** Parameters: 262038032Speter** name -- the name to translate. 2621120256Sgshapiro** m -- the mailer that we want to do rewriting relative to. 262238032Speter** flags -- fine tune operations. 262338032Speter** pstat -- pointer to status word. 262438032Speter** e -- the current envelope. 262538032Speter** 262638032Speter** Returns: 262738032Speter** the text string representing this address relative to 262838032Speter** the receiving mailer. 262938032Speter** 263038032Speter** Side Effects: 263138032Speter** none. 263238032Speter** 263338032Speter** Warnings: 263438032Speter** The text string returned is tucked away locally; 263538032Speter** copy it if you intend to save it. 263638032Speter*/ 263738032Speter 263838032Speterchar * 263938032Speterremotename(name, m, flags, pstat, e) 264038032Speter char *name; 264138032Speter struct mailer *m; 264238032Speter int flags; 264338032Speter int *pstat; 264438032Speter register ENVELOPE *e; 264538032Speter{ 264638032Speter register char **pvp; 264790792Sgshapiro char *SM_NONVOLATILE fancy; 264890792Sgshapiro char *oldg; 264938032Speter int rwset; 265038032Speter static char buf[MAXNAME + 1]; 265138032Speter char lbuf[MAXNAME + 1]; 265238032Speter char pvpbuf[PSBUFSIZE]; 265364562Sgshapiro char addrtype[4]; 265438032Speter 265538032Speter if (tTd(12, 1)) 2656168515Sgshapiro { 2657168515Sgshapiro sm_dprintf("remotename("); 2658168515Sgshapiro xputs(sm_debug_file(), name); 2659168515Sgshapiro sm_dprintf(")\n"); 2660168515Sgshapiro } 266138032Speter 266238032Speter /* don't do anything if we are tagging it as special */ 266338032Speter if (bitset(RF_SENDERADDR, flags)) 266464562Sgshapiro { 266538032Speter rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset 266638032Speter : m->m_se_rwset; 266764562Sgshapiro addrtype[2] = 's'; 266864562Sgshapiro } 266938032Speter else 267064562Sgshapiro { 267138032Speter rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset 267238032Speter : m->m_re_rwset; 267364562Sgshapiro addrtype[2] = 'r'; 267464562Sgshapiro } 267538032Speter if (rwset < 0) 267664562Sgshapiro return name; 267764562Sgshapiro addrtype[1] = ' '; 267864562Sgshapiro addrtype[3] = '\0'; 267964562Sgshapiro addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e'; 268090792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype); 268138032Speter 268238032Speter /* 268338032Speter ** Do a heuristic crack of this name to extract any comment info. 268438032Speter ** This will leave the name as a comment and a $g macro. 268538032Speter */ 268638032Speter 268738032Speter if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags)) 268838032Speter fancy = "\201g"; 268938032Speter else 2690111823Sgshapiro fancy = crackaddr(name, e); 269138032Speter 269238032Speter /* 269338032Speter ** Turn the name into canonical form. 269438032Speter ** Normally this will be RFC 822 style, i.e., "user@domain". 269538032Speter ** If this only resolves to "user", and the "C" flag is 269638032Speter ** specified in the sending mailer, then the sender's 269738032Speter ** domain will be appended. 269838032Speter */ 269938032Speter 2700168515Sgshapiro pvp = prescan(name, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, false); 270138032Speter if (pvp == NULL) 270264562Sgshapiro return name; 270390792Sgshapiro if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 270438032Speter *pstat = EX_TEMPFAIL; 270538032Speter if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) 270638032Speter { 270738032Speter /* append from domain to this address */ 270838032Speter register char **pxp = pvp; 270964562Sgshapiro int l = MAXATOM; /* size of buffer for pvp */ 271038032Speter 271138032Speter /* see if there is an "@domain" in the current name */ 271238032Speter while (*pxp != NULL && strcmp(*pxp, "@") != 0) 271364562Sgshapiro { 271438032Speter pxp++; 271564562Sgshapiro --l; 271664562Sgshapiro } 271738032Speter if (*pxp == NULL) 271838032Speter { 271938032Speter /* no.... append the "@domain" from the sender */ 272038032Speter register char **qxq = e->e_fromdomain; 272138032Speter 272238032Speter while ((*pxp++ = *qxq++) != NULL) 272364562Sgshapiro { 272464562Sgshapiro if (--l <= 0) 272564562Sgshapiro { 272664562Sgshapiro *--pxp = NULL; 272764562Sgshapiro usrerr("553 5.1.0 remotename: too many tokens"); 272864562Sgshapiro *pstat = EX_UNAVAILABLE; 272964562Sgshapiro break; 273064562Sgshapiro } 273164562Sgshapiro } 273290792Sgshapiro if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 273338032Speter *pstat = EX_TEMPFAIL; 273438032Speter } 273538032Speter } 273638032Speter 273738032Speter /* 273838032Speter ** Do more specific rewriting. 273938032Speter ** Rewrite using ruleset 1 or 2 depending on whether this is 274038032Speter ** a sender address or not. 274138032Speter ** Then run it through any receiving-mailer-specific rulesets. 274238032Speter */ 274338032Speter 274438032Speter if (bitset(RF_SENDERADDR, flags)) 274538032Speter { 274690792Sgshapiro if (REWRITE(pvp, 1, e) == EX_TEMPFAIL) 274738032Speter *pstat = EX_TEMPFAIL; 274838032Speter } 274938032Speter else 275038032Speter { 275190792Sgshapiro if (REWRITE(pvp, 2, e) == EX_TEMPFAIL) 275238032Speter *pstat = EX_TEMPFAIL; 275338032Speter } 275438032Speter if (rwset > 0) 275538032Speter { 275690792Sgshapiro if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL) 275738032Speter *pstat = EX_TEMPFAIL; 275838032Speter } 275938032Speter 276038032Speter /* 276138032Speter ** Do any final sanitation the address may require. 276238032Speter ** This will normally be used to turn internal forms 276338032Speter ** (e.g., user@host.LOCAL) into external form. This 276438032Speter ** may be used as a default to the above rules. 276538032Speter */ 276638032Speter 276790792Sgshapiro if (REWRITE(pvp, 4, e) == EX_TEMPFAIL) 276838032Speter *pstat = EX_TEMPFAIL; 276938032Speter 277038032Speter /* 277138032Speter ** Now restore the comment information we had at the beginning. 277238032Speter */ 277338032Speter 2774168515Sgshapiro cataddr(pvp, NULL, lbuf, sizeof(lbuf), '\0', false); 277590792Sgshapiro oldg = macget(&e->e_macro, 'g'); 277690792Sgshapiro macset(&e->e_macro, 'g', lbuf); 277738032Speter 277890792Sgshapiro SM_TRY 277990792Sgshapiro /* need to make sure route-addrs have <angle brackets> */ 278090792Sgshapiro if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') 2781168515Sgshapiro expand("<\201g>", buf, sizeof(buf), e); 278290792Sgshapiro else 2783168515Sgshapiro expand(fancy, buf, sizeof(buf), e); 278490792Sgshapiro SM_FINALLY 278590792Sgshapiro macset(&e->e_macro, 'g', oldg); 278690792Sgshapiro SM_END_TRY 278738032Speter 278838032Speter if (tTd(12, 1)) 2789168515Sgshapiro { 2790168515Sgshapiro sm_dprintf("remotename => `"); 2791168515Sgshapiro xputs(sm_debug_file(), buf); 2792168515Sgshapiro sm_dprintf("'\n"); 2793168515Sgshapiro } 279464562Sgshapiro return buf; 279538032Speter} 279690792Sgshapiro/* 279738032Speter** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 279838032Speter** 279938032Speter** Parameters: 280038032Speter** a -- the address to map (but just the user name part). 280138032Speter** sendq -- the sendq in which to install any replacement 280238032Speter** addresses. 280338032Speter** aliaslevel -- the alias nesting depth. 280438032Speter** e -- the envelope. 280538032Speter** 280638032Speter** Returns: 280738032Speter** none. 280838032Speter*/ 280938032Speter 281038032Speter#define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\ 281138032Speter Q_PINGFLAGS|QHASNOTIFY|\ 281290792Sgshapiro QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\ 281390792Sgshapiro QBYTRACE|QBYNDELAY|QBYNRELAY) 281438032Speter 281538032Spetervoid 281638032Spetermaplocaluser(a, sendq, aliaslevel, e) 281738032Speter register ADDRESS *a; 281838032Speter ADDRESS **sendq; 281938032Speter int aliaslevel; 282038032Speter ENVELOPE *e; 282138032Speter{ 282238032Speter register char **pvp; 282390792Sgshapiro register ADDRESS *SM_NONVOLATILE a1 = NULL; 282438032Speter char pvpbuf[PSBUFSIZE]; 282538032Speter 282638032Speter if (tTd(29, 1)) 282738032Speter { 282890792Sgshapiro sm_dprintf("maplocaluser: "); 2829132943Sgshapiro printaddr(sm_debug_file(), a, false); 283038032Speter } 2831168515Sgshapiro pvp = prescan(a->q_user, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, 2832168515Sgshapiro false); 283338032Speter if (pvp == NULL) 283438032Speter { 283538032Speter if (tTd(29, 9)) 283690792Sgshapiro sm_dprintf("maplocaluser: cannot prescan %s\n", 283764562Sgshapiro a->q_user); 283838032Speter return; 283938032Speter } 284038032Speter 284190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', a->q_host); 284290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', a->q_user); 284390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', a->q_home); 284464562Sgshapiro 284590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); 284690792Sgshapiro if (REWRITE(pvp, 5, e) == EX_TEMPFAIL) 284738032Speter { 284838032Speter if (tTd(29, 9)) 284990792Sgshapiro sm_dprintf("maplocaluser: rewrite tempfail\n"); 285064562Sgshapiro a->q_state = QS_QUEUEUP; 285138032Speter a->q_status = "4.4.3"; 285238032Speter return; 285338032Speter } 285438032Speter if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 285538032Speter { 285638032Speter if (tTd(29, 9)) 285790792Sgshapiro sm_dprintf("maplocaluser: doesn't resolve\n"); 285838032Speter return; 285938032Speter } 286038032Speter 286190792Sgshapiro SM_TRY 286290792Sgshapiro a1 = buildaddr(pvp, NULL, 0, e); 286390792Sgshapiro SM_EXCEPT(exc, "E:mta.quickabort") 286490792Sgshapiro 286590792Sgshapiro /* 286690792Sgshapiro ** mark address as bad, S5 returned an error 286790792Sgshapiro ** and we gave that back to the SMTP client. 286890792Sgshapiro */ 286990792Sgshapiro 287090792Sgshapiro a->q_state = QS_DONTSEND; 287190792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 2); 287290792Sgshapiro SM_END_TRY 287390792Sgshapiro 287438032Speter /* if non-null, mailer destination specified -- has it changed? */ 287538032Speter if (a1 == NULL || sameaddr(a, a1)) 287638032Speter { 287738032Speter if (tTd(29, 9)) 287890792Sgshapiro sm_dprintf("maplocaluser: address unchanged\n"); 287938032Speter return; 288038032Speter } 288138032Speter 288238032Speter /* make new address take on flags and print attributes of old */ 288338032Speter a1->q_flags &= ~Q_COPYFLAGS; 288438032Speter a1->q_flags |= a->q_flags & Q_COPYFLAGS; 288590792Sgshapiro a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr); 288690792Sgshapiro a1->q_finalrcpt = a->q_finalrcpt; 288764562Sgshapiro a1->q_orcpt = a->q_orcpt; 288838032Speter 288938032Speter /* mark old address as dead; insert new address */ 289064562Sgshapiro a->q_state = QS_REPLACED; 289138032Speter if (tTd(29, 5)) 289238032Speter { 289390792Sgshapiro sm_dprintf("maplocaluser: QS_REPLACED "); 2894132943Sgshapiro printaddr(sm_debug_file(), a, false); 289538032Speter } 289638032Speter a1->q_alias = a; 289790792Sgshapiro allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e); 289838032Speter (void) recipient(a1, sendq, aliaslevel, e); 289938032Speter} 290090792Sgshapiro/* 290138032Speter** DEQUOTE_INIT -- initialize dequote map 290238032Speter** 290338032Speter** Parameters: 290438032Speter** map -- the internal map structure. 290538032Speter** args -- arguments. 290638032Speter** 290738032Speter** Returns: 290890792Sgshapiro** true. 290938032Speter*/ 291038032Speter 291138032Speterbool 291238032Speterdequote_init(map, args) 291338032Speter MAP *map; 291438032Speter char *args; 291538032Speter{ 291638032Speter register char *p = args; 291738032Speter 291864562Sgshapiro /* there is no check whether there is really an argument */ 291938032Speter map->map_mflags |= MF_KEEPQUOTES; 292038032Speter for (;;) 292138032Speter { 292238032Speter while (isascii(*p) && isspace(*p)) 292338032Speter p++; 292438032Speter if (*p != '-') 292538032Speter break; 292638032Speter switch (*++p) 292738032Speter { 292838032Speter case 'a': 292938032Speter map->map_app = ++p; 293038032Speter break; 293138032Speter 293264562Sgshapiro case 'D': 293364562Sgshapiro map->map_mflags |= MF_DEFER; 293464562Sgshapiro break; 293564562Sgshapiro 293664562Sgshapiro case 'S': 293738032Speter case 's': 293864562Sgshapiro map->map_spacesub = *++p; 293938032Speter break; 294038032Speter } 294138032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 294238032Speter p++; 294338032Speter if (*p != '\0') 294438032Speter *p = '\0'; 294538032Speter } 294638032Speter if (map->map_app != NULL) 294738032Speter map->map_app = newstr(map->map_app); 294838032Speter 294990792Sgshapiro return true; 295038032Speter} 295190792Sgshapiro/* 295238032Speter** DEQUOTE_MAP -- unquote an address 295338032Speter** 295438032Speter** Parameters: 295538032Speter** map -- the internal map structure (ignored). 295638032Speter** name -- the name to dequote. 295738032Speter** av -- arguments (ignored). 295838032Speter** statp -- pointer to status out-parameter. 295938032Speter** 296038032Speter** Returns: 296138032Speter** NULL -- if there were no quotes, or if the resulting 296238032Speter** unquoted buffer would not be acceptable to prescan. 296338032Speter** else -- The dequoted buffer. 296438032Speter*/ 296538032Speter 296638032Speter/* ARGSUSED2 */ 296738032Speterchar * 296838032Speterdequote_map(map, name, av, statp) 296938032Speter MAP *map; 297038032Speter char *name; 297138032Speter char **av; 297238032Speter int *statp; 297338032Speter{ 297438032Speter register char *p; 297538032Speter register char *q; 297638032Speter register char c; 297738032Speter int anglecnt = 0; 297838032Speter int cmntcnt = 0; 297938032Speter int quotecnt = 0; 298038032Speter int spacecnt = 0; 298190792Sgshapiro bool quotemode = false; 298290792Sgshapiro bool bslashmode = false; 298364562Sgshapiro char spacesub = map->map_spacesub; 298438032Speter 298538032Speter for (p = q = name; (c = *p++) != '\0'; ) 298638032Speter { 298738032Speter if (bslashmode) 298838032Speter { 298990792Sgshapiro bslashmode = false; 299038032Speter *q++ = c; 299138032Speter continue; 299238032Speter } 299338032Speter 299438032Speter if (c == ' ' && spacesub != '\0') 299538032Speter c = spacesub; 299638032Speter 299738032Speter switch (c) 299838032Speter { 299938032Speter case '\\': 300090792Sgshapiro bslashmode = true; 300138032Speter break; 300238032Speter 300338032Speter case '(': 300438032Speter cmntcnt++; 300538032Speter break; 300638032Speter 300738032Speter case ')': 300838032Speter if (cmntcnt-- <= 0) 300938032Speter return NULL; 301038032Speter break; 301138032Speter 301238032Speter case ' ': 301364562Sgshapiro case '\t': 301438032Speter spacecnt++; 301538032Speter break; 301638032Speter } 301738032Speter 301838032Speter if (cmntcnt > 0) 301938032Speter { 302038032Speter *q++ = c; 302138032Speter continue; 302238032Speter } 302338032Speter 302438032Speter switch (c) 302538032Speter { 302638032Speter case '"': 302738032Speter quotemode = !quotemode; 302838032Speter quotecnt++; 302938032Speter continue; 303038032Speter 303138032Speter case '<': 303238032Speter anglecnt++; 303338032Speter break; 303438032Speter 303538032Speter case '>': 303638032Speter if (anglecnt-- <= 0) 303738032Speter return NULL; 303838032Speter break; 303938032Speter } 304038032Speter *q++ = c; 304138032Speter } 304238032Speter 304338032Speter if (anglecnt != 0 || cmntcnt != 0 || bslashmode || 304438032Speter quotemode || quotecnt <= 0 || spacecnt != 0) 304538032Speter return NULL; 304638032Speter *q++ = '\0'; 304738032Speter return map_rewrite(map, name, strlen(name), NULL); 304838032Speter} 304990792Sgshapiro/* 305038032Speter** RSCHECK -- check string(s) for validity using rewriting sets 305138032Speter** 305238032Speter** Parameters: 305338032Speter** rwset -- the rewriting set to use. 305438032Speter** p1 -- the first string to check. 305538032Speter** p2 -- the second string to check -- may be null. 305638032Speter** e -- the current envelope. 3057102528Sgshapiro** flags -- control some behavior, see RSF_ in sendmail.h 305890792Sgshapiro** logl -- logging level. 305971345Sgshapiro** host -- NULL or relay host. 306090792Sgshapiro** logid -- id for sm_syslog. 3061168515Sgshapiro** addr -- if not NULL and ruleset returns $#error: 3062168515Sgshapiro** store mailer triple here. 306338032Speter** 306438032Speter** Returns: 306538032Speter** EX_OK -- if the rwset doesn't resolve to $#error 306638032Speter** else -- the failure status (message printed) 306738032Speter*/ 306838032Speter 306938032Speterint 3070168515Sgshapirorscheck(rwset, p1, p2, e, flags, logl, host, logid, addr) 307138032Speter char *rwset; 307238032Speter char *p1; 307338032Speter char *p2; 307438032Speter ENVELOPE *e; 3075102528Sgshapiro int flags; 307664562Sgshapiro int logl; 307771345Sgshapiro char *host; 307890792Sgshapiro char *logid; 3079168515Sgshapiro ADDRESS *addr; 308038032Speter{ 308190792Sgshapiro char *volatile buf; 3082157001Sgshapiro size_t bufsize; 308338032Speter int saveexitstat; 308490792Sgshapiro int volatile rstat = EX_OK; 308538032Speter char **pvp; 308638032Speter int rsno; 308790792Sgshapiro bool volatile discard = false; 308838032Speter bool saveQuickAbort = QuickAbort; 308938032Speter bool saveSuprErrs = SuprErrs; 309090792Sgshapiro bool quarantine = false; 309190792Sgshapiro char ubuf[BUFSIZ * 2]; 309238032Speter char buf0[MAXLINE]; 309338032Speter char pvpbuf[PSBUFSIZE]; 309438032Speter extern char MsgBuf[]; 309538032Speter 309638032Speter if (tTd(48, 2)) 309790792Sgshapiro sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1, 309838032Speter p2 == NULL ? "(NULL)" : p2); 309938032Speter 310038032Speter rsno = strtorwset(rwset, NULL, ST_FIND); 310138032Speter if (rsno < 0) 310238032Speter return EX_OK; 310338032Speter 310438032Speter if (p2 != NULL) 310538032Speter { 310638032Speter bufsize = strlen(p1) + strlen(p2) + 2; 3107168515Sgshapiro if (bufsize > sizeof(buf0)) 310890792Sgshapiro buf = sm_malloc_x(bufsize); 310938032Speter else 311038032Speter { 311138032Speter buf = buf0; 3112168515Sgshapiro bufsize = sizeof(buf0); 311338032Speter } 311490792Sgshapiro (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); 311538032Speter } 311638032Speter else 311738032Speter { 311838032Speter bufsize = strlen(p1) + 1; 3119168515Sgshapiro if (bufsize > sizeof(buf0)) 312090792Sgshapiro buf = sm_malloc_x(bufsize); 312138032Speter else 312238032Speter { 312338032Speter buf = buf0; 3124168515Sgshapiro bufsize = sizeof(buf0); 312538032Speter } 312690792Sgshapiro (void) sm_strlcpy(buf, p1, bufsize); 312738032Speter } 312890792Sgshapiro SM_TRY 312938032Speter { 313090792Sgshapiro SuprErrs = true; 313190792Sgshapiro QuickAbort = false; 3132168515Sgshapiro pvp = prescan(buf, '\0', pvpbuf, sizeof(pvpbuf), NULL, 3133168515Sgshapiro bitset(RSF_RMCOMM, flags) ? 3134168515Sgshapiro IntTokenTab : TokTypeNoC, 3135132943Sgshapiro bitset(RSF_RMCOMM, flags) ? false : true); 313690792Sgshapiro SuprErrs = saveSuprErrs; 313790792Sgshapiro if (pvp == NULL) 313890792Sgshapiro { 313990792Sgshapiro if (tTd(48, 2)) 314090792Sgshapiro sm_dprintf("rscheck: cannot prescan input\n"); 314190792Sgshapiro /* 314290792Sgshapiro syserr("rscheck: cannot prescan input: \"%s\"", 314390792Sgshapiro shortenstring(buf, MAXSHORTSTR)); 314490792Sgshapiro rstat = EX_DATAERR; 314590792Sgshapiro */ 314690792Sgshapiro goto finis; 314790792Sgshapiro } 3148102528Sgshapiro if (bitset(RSF_UNSTRUCTURED, flags)) 3149102528Sgshapiro SuprErrs = true; 315090792Sgshapiro (void) REWRITE(pvp, rsno, e); 3151102528Sgshapiro if (bitset(RSF_UNSTRUCTURED, flags)) 3152102528Sgshapiro SuprErrs = saveSuprErrs; 315390792Sgshapiro if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || 315490792Sgshapiro pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 && 315590792Sgshapiro strcmp(pvp[1], "discard") != 0)) 315690792Sgshapiro { 315790792Sgshapiro goto finis; 315890792Sgshapiro } 315966494Sgshapiro 316090792Sgshapiro if (strcmp(pvp[1], "discard") == 0) 316190792Sgshapiro { 316290792Sgshapiro if (tTd(48, 2)) 316390792Sgshapiro sm_dprintf("rscheck: discard mailer selected\n"); 316490792Sgshapiro e->e_flags |= EF_DISCARD; 316590792Sgshapiro discard = true; 316690792Sgshapiro } 316790792Sgshapiro else if (strcmp(pvp[1], "error") == 0 && 316890792Sgshapiro pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST && 316990792Sgshapiro pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0) 317090792Sgshapiro { 317190792Sgshapiro if (pvp[4] == NULL || 317290792Sgshapiro (pvp[4][0] & 0377) != CANONUSER || 317390792Sgshapiro pvp[5] == NULL) 317490792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 317590792Sgshapiro rwset); 317690792Sgshapiro else 317790792Sgshapiro { 317890792Sgshapiro cataddr(&(pvp[5]), NULL, ubuf, 3179168515Sgshapiro sizeof(ubuf), ' ', true); 318090792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 318190792Sgshapiro ubuf); 318290792Sgshapiro } 318390792Sgshapiro macdefine(&e->e_macro, A_PERM, 318490792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 318590792Sgshapiro quarantine = true; 318690792Sgshapiro } 318790792Sgshapiro else 318890792Sgshapiro { 3189168515Sgshapiro auto ADDRESS a1; 319090792Sgshapiro int savelogusrerrs = LogUsrErrs; 319190792Sgshapiro static bool logged = false; 319290792Sgshapiro 319390792Sgshapiro /* got an error -- process it */ 319490792Sgshapiro saveexitstat = ExitStat; 319590792Sgshapiro LogUsrErrs = false; 319690792Sgshapiro (void) buildaddr(pvp, &a1, 0, e); 3197168515Sgshapiro if (addr != NULL) 3198168515Sgshapiro { 3199168515Sgshapiro addr->q_mailer = a1.q_mailer; 3200168515Sgshapiro addr->q_user = a1.q_user; 3201168515Sgshapiro addr->q_host = a1.q_host; 3202168515Sgshapiro } 320390792Sgshapiro LogUsrErrs = savelogusrerrs; 320490792Sgshapiro rstat = ExitStat; 320590792Sgshapiro ExitStat = saveexitstat; 320690792Sgshapiro if (!logged) 320790792Sgshapiro { 3208102528Sgshapiro if (bitset(RSF_COUNT, flags)) 320990792Sgshapiro markstats(e, &a1, STATS_REJECT); 321090792Sgshapiro logged = true; 321190792Sgshapiro } 321290792Sgshapiro } 321390792Sgshapiro 321490792Sgshapiro if (LogLevel > logl) 321590792Sgshapiro { 321690792Sgshapiro char *relay; 321790792Sgshapiro char *p; 321890792Sgshapiro char lbuf[MAXLINE]; 321990792Sgshapiro 322090792Sgshapiro p = lbuf; 322190792Sgshapiro if (p2 != NULL) 322290792Sgshapiro { 322390792Sgshapiro (void) sm_snprintf(p, SPACELEFT(lbuf, p), 322490792Sgshapiro ", arg2=%s", 322590792Sgshapiro p2); 322690792Sgshapiro p += strlen(p); 322790792Sgshapiro } 322890792Sgshapiro 322990792Sgshapiro if (host != NULL) 323090792Sgshapiro relay = host; 323190792Sgshapiro else 323290792Sgshapiro relay = macvalue('_', e); 323390792Sgshapiro if (relay != NULL) 323490792Sgshapiro { 323590792Sgshapiro (void) sm_snprintf(p, SPACELEFT(lbuf, p), 323690792Sgshapiro ", relay=%s", relay); 323790792Sgshapiro p += strlen(p); 323890792Sgshapiro } 323990792Sgshapiro *p = '\0'; 324090792Sgshapiro if (discard) 324190792Sgshapiro sm_syslog(LOG_NOTICE, logid, 324290792Sgshapiro "ruleset=%s, arg1=%s%s, discard", 324390792Sgshapiro rwset, p1, lbuf); 324490792Sgshapiro else if (quarantine) 324590792Sgshapiro sm_syslog(LOG_NOTICE, logid, 324690792Sgshapiro "ruleset=%s, arg1=%s%s, quarantine=%s", 324790792Sgshapiro rwset, p1, lbuf, ubuf); 324890792Sgshapiro else 324990792Sgshapiro sm_syslog(LOG_NOTICE, logid, 325090792Sgshapiro "ruleset=%s, arg1=%s%s, reject=%s", 325190792Sgshapiro rwset, p1, lbuf, MsgBuf); 325290792Sgshapiro } 325390792Sgshapiro 325490792Sgshapiro finis: ; 325573188Sgshapiro } 325690792Sgshapiro SM_FINALLY 325738032Speter { 325890792Sgshapiro /* clean up */ 325990792Sgshapiro if (buf != buf0) 326090792Sgshapiro sm_free(buf); 326190792Sgshapiro QuickAbort = saveQuickAbort; 326238032Speter } 326390792Sgshapiro SM_END_TRY 326438032Speter 326590792Sgshapiro setstat(rstat); 326690792Sgshapiro 326790792Sgshapiro /* rulesets don't set errno */ 326890792Sgshapiro errno = 0; 326990792Sgshapiro if (rstat != EX_OK && QuickAbort) 327090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 2); 327190792Sgshapiro return rstat; 327290792Sgshapiro} 327390792Sgshapiro/* 327490792Sgshapiro** RSCAP -- call rewriting set to return capabilities 327590792Sgshapiro** 327690792Sgshapiro** Parameters: 327790792Sgshapiro** rwset -- the rewriting set to use. 327890792Sgshapiro** p1 -- the first string to check. 327990792Sgshapiro** p2 -- the second string to check -- may be null. 328090792Sgshapiro** e -- the current envelope. 328190792Sgshapiro** pvp -- pointer to token vector. 328290792Sgshapiro** pvpbuf -- buffer space. 3283120256Sgshapiro** size -- size of buffer space. 328490792Sgshapiro** 328590792Sgshapiro** Returns: 328690792Sgshapiro** EX_UNAVAILABLE -- ruleset doesn't exist. 328790792Sgshapiro** EX_DATAERR -- prescan() failed. 328890792Sgshapiro** EX_OK -- rewrite() was successful. 328990792Sgshapiro** else -- return status from rewrite(). 329090792Sgshapiro*/ 329190792Sgshapiro 329290792Sgshapiroint 329390792Sgshapirorscap(rwset, p1, p2, e, pvp, pvpbuf, size) 329490792Sgshapiro char *rwset; 329590792Sgshapiro char *p1; 329690792Sgshapiro char *p2; 329790792Sgshapiro ENVELOPE *e; 329890792Sgshapiro char ***pvp; 329990792Sgshapiro char *pvpbuf; 330090792Sgshapiro int size; 330190792Sgshapiro{ 330290792Sgshapiro char *volatile buf; 3303157001Sgshapiro size_t bufsize; 330490792Sgshapiro int volatile rstat = EX_OK; 330590792Sgshapiro int rsno; 330690792Sgshapiro bool saveQuickAbort = QuickAbort; 330790792Sgshapiro bool saveSuprErrs = SuprErrs; 330890792Sgshapiro char buf0[MAXLINE]; 330990792Sgshapiro extern char MsgBuf[]; 331090792Sgshapiro 331190792Sgshapiro if (tTd(48, 2)) 331290792Sgshapiro sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1, 331390792Sgshapiro p2 == NULL ? "(NULL)" : p2); 331490792Sgshapiro 3315159609Sgshapiro SM_REQUIRE(pvp != NULL); 331690792Sgshapiro rsno = strtorwset(rwset, NULL, ST_FIND); 331790792Sgshapiro if (rsno < 0) 331890792Sgshapiro return EX_UNAVAILABLE; 331990792Sgshapiro 332090792Sgshapiro if (p2 != NULL) 332138032Speter { 332290792Sgshapiro bufsize = strlen(p1) + strlen(p2) + 2; 3323168515Sgshapiro if (bufsize > sizeof(buf0)) 332490792Sgshapiro buf = sm_malloc_x(bufsize); 332590792Sgshapiro else 332690792Sgshapiro { 332790792Sgshapiro buf = buf0; 3328168515Sgshapiro bufsize = sizeof(buf0); 332990792Sgshapiro } 333090792Sgshapiro (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); 333138032Speter } 333264562Sgshapiro else 333338032Speter { 333490792Sgshapiro bufsize = strlen(p1) + 1; 3335168515Sgshapiro if (bufsize > sizeof(buf0)) 333690792Sgshapiro buf = sm_malloc_x(bufsize); 333790792Sgshapiro else 333838032Speter { 333990792Sgshapiro buf = buf0; 3340168515Sgshapiro bufsize = sizeof(buf0); 334138032Speter } 334290792Sgshapiro (void) sm_strlcpy(buf, p1, bufsize); 334338032Speter } 334490792Sgshapiro SM_TRY 334538032Speter { 334690792Sgshapiro SuprErrs = true; 334790792Sgshapiro QuickAbort = false; 3348168515Sgshapiro *pvp = prescan(buf, '\0', pvpbuf, size, NULL, IntTokenTab, 3349168515Sgshapiro false); 335090792Sgshapiro if (*pvp != NULL) 3351120256Sgshapiro rstat = rewrite(*pvp, rsno, 0, e, size); 335271345Sgshapiro else 335338032Speter { 335490792Sgshapiro if (tTd(48, 2)) 335590792Sgshapiro sm_dprintf("rscap: cannot prescan input\n"); 335690792Sgshapiro rstat = EX_DATAERR; 335738032Speter } 335838032Speter } 335990792Sgshapiro SM_FINALLY 336090792Sgshapiro { 336190792Sgshapiro /* clean up */ 336290792Sgshapiro if (buf != buf0) 336390792Sgshapiro sm_free(buf); 336490792Sgshapiro SuprErrs = saveSuprErrs; 336590792Sgshapiro QuickAbort = saveQuickAbort; 336638032Speter 336790792Sgshapiro /* prevent information leak, this may contain rewrite error */ 336890792Sgshapiro MsgBuf[0] = '\0'; 336990792Sgshapiro } 337090792Sgshapiro SM_END_TRY 337138032Speter return rstat; 337238032Speter} 3373