1 /*
   2  * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers.
   3  *      All rights reserved.
   4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
   5  * Copyright (c) 1988, 1993
   6  *      The Regents of the University of California.  All rights reserved.
   7  *
   8  * By using this file, you agree to the terms and conditions set
   9  * forth in the LICENSE file which can be found at the top level of
  10  * the sendmail distribution.
  11  *
  12  */
  13 
  14 #pragma ident   "%Z%%M% %I%     %E% SMI"
  15 
  16 #include <sendmail.h>
  17 
  18 SM_RCSID("@(#)$Id: parseaddr.c,v 8.403 2008/02/08 02:27:35 ca Exp $")
  19 
  20 #include <sm/sendmail.h>
  21 #include "map.h"
  22 
  23 static void     allocaddr __P((ADDRESS *, int, char *, ENVELOPE *));
  24 static int      callsubr __P((char**, int, ENVELOPE *));
  25 static char     *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *));
  26 static ADDRESS  *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *));
  27 static bool     hasctrlchar __P((register char *, bool, bool));
  28 
  29 /* replacement for illegal characters in addresses */
  30 #define BAD_CHAR_REPLACEMENT    '?'
  31 
  32 /*
  33 **  PARSEADDR -- Parse an address
  34 **
  35 **      Parses an address and breaks it up into three parts: a
  36 **      net to transmit the message on, the host to transmit it
  37 **      to, and a user on that host.  These are loaded into an
  38 **      ADDRESS header with the values squirreled away if necessary.
  39 **      The "user" part may not be a real user; the process may
  40 **      just reoccur on that machine.  For example, on a machine
  41 **      with an arpanet connection, the address
  42 **              csvax.bill@berkeley
  43 **      will break up to a "user" of 'csvax.bill' and a host
  44 **      of 'berkeley' -- to be transmitted over the arpanet.
  45 **
  46 **      Parameters:
  47 **              addr -- the address to parse.
  48 **              a -- a pointer to the address descriptor buffer.
  49 **                      If NULL, an address will be created.
  50 **              flags -- describe detail for parsing.  See RF_ definitions
  51 **                      in sendmail.h.
  52 **              delim -- the character to terminate the address, passed
  53 **                      to prescan.
  54 **              delimptr -- if non-NULL, set to the location of the
  55 **                      delim character that was found.
  56 **              e -- the envelope that will contain this address.
  57 **              isrcpt -- true if the address denotes a recipient; false
  58 **                      indicates a sender.
  59 **
  60 **      Returns:
  61 **              A pointer to the address descriptor header (`a' if
  62 **                      `a' is non-NULL).
  63 **              NULL on error.
  64 **
  65 **      Side Effects:
  66 **              e->e_to = addr
  67 */
  68 
  69 /* following delimiters are inherent to the internal algorithms */
  70 #define DELIMCHARS      "()<>,;\r\n"      /* default word delimiters */
  71 
  72 ADDRESS *
  73 parseaddr(addr, a, flags, delim, delimptr, e, isrcpt)
  74         char *addr;
  75         register ADDRESS *a;
  76         int flags;
  77         int delim;
  78         char **delimptr;
  79         register ENVELOPE *e;
  80         bool isrcpt;
  81 {
  82         char **pvp;
  83         auto char *delimptrbuf;
  84         bool qup;
  85         char pvpbuf[PSBUFSIZE];
  86 
  87         /*
  88         **  Initialize and prescan address.
  89         */
  90 
  91         e->e_to = addr;
  92         if (tTd(20, 1))
  93                 sm_dprintf("\n--parseaddr(%s)\n", addr);
  94 
  95         if (delimptr == NULL)
  96                 delimptr = &delimptrbuf;
  97 
  98         pvp = prescan(addr, delim, pvpbuf, sizeof(pvpbuf), delimptr,
  99                         ExtTokenTab, false);
 100         if (pvp == NULL)
 101         {
 102                 if (tTd(20, 1))
 103                         sm_dprintf("parseaddr-->NULL\n");
 104                 return NULL;
 105         }
 106 
 107         if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr, isrcpt))
 108         {
 109                 if (tTd(20, 1))
 110                         sm_dprintf("parseaddr-->bad address\n");
 111                 return NULL;
 112         }
 113 
 114         /*
 115         **  Save addr if we are going to have to.
 116         **
 117         **      We have to do this early because there is a chance that
 118         **      the map lookups in the rewriting rules could clobber
 119         **      static memory somewhere.
 120         */
 121 
 122         if (bitset(RF_COPYPADDR, flags) && addr != NULL)
 123         {
 124                 char savec = **delimptr;
 125 
 126                 if (savec != '\0')
 127                         **delimptr = '\0';
 128                 e->e_to = addr = sm_rpool_strdup_x(e->e_rpool, addr);
 129                 if (savec != '\0')
 130                         **delimptr = savec;
 131         }
 132 
 133         /*
 134         **  Apply rewriting rules.
 135         **      Ruleset 0 does basic parsing.  It must resolve.
 136         */
 137 
 138         qup = false;
 139         if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
 140                 qup = true;
 141         if (REWRITE(pvp, 0, e) == EX_TEMPFAIL)
 142                 qup = true;
 143 
 144         /*
 145         **  Build canonical address from pvp.
 146         */
 147 
 148         a = buildaddr(pvp, a, flags, e);
 149 
 150         if (hasctrlchar(a->q_user, isrcpt, true))
 151         {
 152                 if (tTd(20, 1))
 153                         sm_dprintf("parseaddr-->bad q_user\n");
 154 
 155                 /*
 156                 **  Just mark the address as bad so DSNs work.
 157                 **  hasctrlchar() has to make sure that the address
 158                 **  has been sanitized, e.g., shortened.
 159                 */
 160 
 161                 a->q_state = QS_BADADDR;
 162         }
 163 
 164         /*
 165         **  Make local copies of the host & user and then
 166         **  transport them out.
 167         */
 168 
 169         allocaddr(a, flags, addr, e);
 170         if (QS_IS_BADADDR(a->q_state))
 171         {
 172                 /* weed out bad characters in the printable address too */
 173                 (void) hasctrlchar(a->q_paddr, isrcpt, false);
 174                 return a;
 175         }
 176 
 177         /*
 178         **  Select a queue directory for recipient addresses.
 179         **      This is done here and in split_across_queue_groups(),
 180         **      but the latter applies to addresses after aliasing,
 181         **      and only if splitting is done.
 182         */
 183 
 184         if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) &&
 185             !bitset(RF_SENDERADDR|RF_HEADERADDR|RF_RM_ADDR, flags) &&
 186             OpMode != MD_INITALIAS)
 187         {
 188                 int r;
 189 
 190                 /* call ruleset which should return a queue group name */
 191                 r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf,
 192                           sizeof(pvpbuf));
 193                 if (r == EX_OK &&
 194                     pvp != NULL && pvp[0] != NULL &&
 195                     (pvp[0][0] & 0377) == CANONNET &&
 196                     pvp[1] != NULL && pvp[1][0] != '\0')
 197                 {
 198                         r = name2qid(pvp[1]);
 199                         if (r == NOQGRP && LogLevel > 10)
 200                                 sm_syslog(LOG_INFO, NOQID,
 201                                         "can't find queue group name %s, selection ignored",
 202                                         pvp[1]);
 203                         if (tTd(20, 4) && r != NOQGRP)
 204                                 sm_syslog(LOG_INFO, NOQID,
 205                                         "queue group name %s -> %d",
 206                                         pvp[1], r);
 207                         a->q_qgrp = r == NOQGRP ? ENVQGRP : r;
 208                 }
 209         }
 210 
 211         /*
 212         **  If there was a parsing failure, mark it for queueing.
 213         */
 214 
 215         if (qup && OpMode != MD_INITALIAS)
 216         {
 217                 char *msg = "Transient parse error -- message queued for future delivery";
 218 
 219                 if (e->e_sendmode == SM_DEFER)
 220                         msg = "Deferring message until queue run";
 221                 if (tTd(20, 1))
 222                         sm_dprintf("parseaddr: queueing message\n");
 223                 message(msg);
 224                 if (e->e_message == NULL && e->e_sendmode != SM_DEFER)
 225                         e->e_message = sm_rpool_strdup_x(e->e_rpool, msg);
 226                 a->q_state = QS_QUEUEUP;
 227                 a->q_status = "4.4.3";
 228         }
 229 
 230         /*
 231         **  Compute return value.
 232         */
 233 
 234         if (tTd(20, 1))
 235         {
 236                 sm_dprintf("parseaddr-->");
 237                 printaddr(sm_debug_file(), a, false);
 238         }
 239 
 240         return a;
 241 }
 242 /*
 243 **  INVALIDADDR -- check for address containing characters used for macros
 244 **
 245 **      Parameters:
 246 **              addr -- the address to check.
 247 **              delimptr -- if non-NULL: end of address to check, i.e.,
 248 **                      a pointer in the address string.
 249 **              isrcpt -- true iff the address is for a recipient.
 250 **
 251 **      Returns:
 252 **              true -- if the address has characters that are reservered
 253 **                      for macros or is too long.
 254 **              false -- otherwise.
 255 */
 256 
 257 bool
 258 invalidaddr(addr, delimptr, isrcpt)
 259         register char *addr;
 260         char *delimptr;
 261         bool isrcpt;
 262 {
 263         bool result = false;
 264         char savedelim = '\0';
 265         char *b = addr;
 266         int len = 0;
 267 
 268         if (delimptr != NULL)
 269         {
 270                 /* delimptr points to the end of the address to test */
 271                 savedelim = *delimptr;
 272                 if (savedelim != '\0')  /* if that isn't '\0' already: */
 273                         *delimptr = '\0';       /* set it */
 274         }
 275         for (; *addr != '\0'; addr++)
 276         {
 277                 if (!EightBitAddrOK && (*addr & 0340) == 0200)
 278                 {
 279                         setstat(EX_USAGE);
 280                         result = true;
 281                         *addr = BAD_CHAR_REPLACEMENT;
 282                 }
 283                 if (++len > MAXNAME - 1)
 284                 {
 285                         char saved = *addr;
 286 
 287                         *addr = '\0';
 288                         usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
 289                                b, MAXNAME - 1);
 290                         *addr = saved;
 291                         result = true;
 292                         goto delim;
 293                 }
 294         }
 295         if (result)
 296         {
 297                 if (isrcpt)
 298                         usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"",
 299                                b);
 300                 else
 301                         usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"",
 302                                b);
 303         }
 304 delim:
 305         if (delimptr != NULL && savedelim != '\0')
 306                 *delimptr = savedelim;  /* restore old character at delimptr */
 307         return result;
 308 }
 309 /*
 310 **  HASCTRLCHAR -- check for address containing meta-characters
 311 **
 312 **  Checks that the address contains no meta-characters, and contains
 313 **  no "non-printable" characters unless they are quoted or escaped.
 314 **  Quoted or escaped characters are literals.
 315 **
 316 **      Parameters:
 317 **              addr -- the address to check.
 318 **              isrcpt -- true if the address is for a recipient; false
 319 **                      indicates a from.
 320 **              complain -- true if an error should issued if the address
 321 **                      is invalid and should be "repaired".
 322 **
 323 **      Returns:
 324 **              true -- if the address has any "wierd" characters or
 325 **                      non-printable characters or if a quote is unbalanced.
 326 **              false -- otherwise.
 327 */
 328 
 329 static bool
 330 hasctrlchar(addr, isrcpt, complain)
 331         register char *addr;
 332         bool isrcpt, complain;
 333 {
 334         bool quoted = false;
 335         int len = 0;
 336         char *result = NULL;
 337         char *b = addr;
 338 
 339         if (addr == NULL)
 340                 return false;
 341         for (; *addr != '\0'; addr++)
 342         {
 343                 if (++len > MAXNAME - 1)
 344                 {
 345                         if (complain)
 346                         {
 347                                 (void) shorten_rfc822_string(b, MAXNAME - 1);
 348                                 usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)",
 349                                        b, MAXNAME - 1);
 350                                 return true;
 351                         }
 352                         result = "too long";
 353                 }
 354                 if (!EightBitAddrOK && !quoted && (*addr < 32 || *addr == 127))
 355                 {
 356                         result = "non-printable character";
 357                         *addr = BAD_CHAR_REPLACEMENT;
 358                         continue;
 359                 }
 360                 if (*addr == '"')
 361                         quoted = !quoted;
 362                 else if (*addr == '\\')
 363                 {
 364                         /* XXX Generic problem: no '\0' in strings. */
 365                         if (*++addr == '\0')
 366                         {
 367                                 result = "trailing \\ character";
 368                                 *--addr = BAD_CHAR_REPLACEMENT;
 369                                 break;
 370                         }
 371                 }
 372                 if (!EightBitAddrOK && (*addr & 0340) == 0200)
 373                 {
 374                         setstat(EX_USAGE);
 375                         result = "8-bit character";
 376                         *addr = BAD_CHAR_REPLACEMENT;
 377                         continue;
 378                 }
 379         }
 380         if (quoted)
 381                 result = "unbalanced quote"; /* unbalanced quote */
 382         if (result != NULL && complain)
 383         {
 384                 if (isrcpt)
 385                         usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)",
 386                                b, result);
 387                 else
 388                         usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)",
 389                                b, result);
 390         }
 391         return result != NULL;
 392 }
 393 /*
 394 **  ALLOCADDR -- do local allocations of address on demand.
 395 **
 396 **      Also lowercases the host name if requested.
 397 **
 398 **      Parameters:
 399 **              a -- the address to reallocate.
 400 **              flags -- the copy flag (see RF_ definitions in sendmail.h
 401 **                      for a description).
 402 **              paddr -- the printname of the address.
 403 **              e -- envelope
 404 **
 405 **      Returns:
 406 **              none.
 407 **
 408 **      Side Effects:
 409 **              Copies portions of a into local buffers as requested.
 410 */
 411 
 412 static void
 413 allocaddr(a, flags, paddr, e)
 414         register ADDRESS *a;
 415         int flags;
 416         char *paddr;
 417         ENVELOPE *e;
 418 {
 419         if (tTd(24, 4))
 420                 sm_dprintf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr);
 421 
 422         a->q_paddr = paddr;
 423 
 424         if (a->q_user == NULL)
 425                 a->q_user = "";
 426         if (a->q_host == NULL)
 427                 a->q_host = "";
 428 
 429         if (bitset(RF_COPYPARSE, flags))
 430         {
 431                 a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host);
 432                 if (a->q_user != a->q_paddr)
 433                         a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user);
 434         }
 435 
 436         if (a->q_paddr == NULL)
 437                 a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
 438         a->q_qgrp = NOAQGRP;
 439 }
 440 
 441 /*
 442 **  PRESCAN -- Prescan name and make it canonical
 443 **
 444 **      Scans a name and turns it into a set of tokens.  This process
 445 **      deletes blanks and comments (in parentheses) (if the token type
 446 **      for left paren is SPC).
 447 **
 448 **      This routine knows about quoted strings and angle brackets.
 449 **
 450 **      There are certain subtleties to this routine.  The one that
 451 **      comes to mind now is that backslashes on the ends of names
 452 **      are silently stripped off; this is intentional.  The problem
 453 **      is that some versions of sndmsg (like at LBL) set the kill
 454 **      character to something other than @ when reading addresses;
 455 **      so people type "csvax.eric\@berkeley" -- which screws up the
 456 **      berknet mailer.
 457 **
 458 **      Parameters:
 459 **              addr -- the name to chomp.
 460 **              delim -- the delimiter for the address, normally
 461 **                      '\0' or ','; \0 is accepted in any case.
 462 **                      If '\t' then we are reading the .cf file.
 463 **              pvpbuf -- place to put the saved text -- note that
 464 **                      the pointers are static.
 465 **              pvpbsize -- size of pvpbuf.
 466 **              delimptr -- if non-NULL, set to the location of the
 467 **                      terminating delimiter.
 468 **              toktab -- if set, a token table to use for parsing.
 469 **                      If NULL, use the default table.
 470 **              ignore -- if true, ignore unbalanced addresses
 471 **
 472 **      Returns:
 473 **              A pointer to a vector of tokens.
 474 **              NULL on error.
 475 */
 476 
 477 /* states and character types */
 478 #define OPR             0       /* operator */
 479 #define ATM             1       /* atom */
 480 #define QST             2       /* in quoted string */
 481 #define SPC             3       /* chewing up spaces */
 482 #define ONE             4       /* pick up one character */
 483 #define ILL             5       /* illegal character */
 484 
 485 #define NSTATES 6       /* number of states */
 486 #define TYPE            017     /* mask to select state type */
 487 
 488 /* meta bits for table */
 489 #define M               020     /* meta character; don't pass through */
 490 #define B               040     /* cause a break */
 491 #define MB              M|B     /* meta-break */
 492 
 493 static short StateTab[NSTATES][NSTATES] =
 494 {
 495    /*   oldst   chtype>      OPR     ATM     QST     SPC     ONE     ILL     */
 496         /*OPR*/ {       OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|MB  },
 497         /*ATM*/ {       OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,  ILL|MB  },
 498         /*QST*/ {       QST,    QST,    OPR,    QST,    QST,    QST     },
 499         /*SPC*/ {       OPR,    ATM,    QST,    SPC|M,  ONE,    ILL|MB  },
 500         /*ONE*/ {       OPR,    OPR,    OPR,    OPR,    OPR,    ILL|MB  },
 501         /*ILL*/ {       OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,  ILL|M   }
 502 };
 503 
 504 /* these all get modified with the OperatorChars */
 505 
 506 /* token type table for external strings */
 507 unsigned char   ExtTokenTab[256] =
 508 {
 509     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 510         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
 511     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 512         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 513     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 514         SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
 515     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 516         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 517     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 518         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 519     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 520         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 521     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 522         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 523     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 524         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 525 
 526     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 527         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 528     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 529         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 530     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 531         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 532     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 533         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 534     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 535         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 536     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 537         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 538     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 539         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 540     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 541         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM
 542 };
 543 
 544 /* token type table for internal strings */
 545 unsigned char   IntTokenTab[256] =
 546 {
 547     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 548         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
 549     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 550         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 551     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 552         SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM,
 553     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 554         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 555     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 556         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 557     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 558         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 559     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 560         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 561     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 562         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 563 
 564     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 565         OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
 566     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 567         OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
 568     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 569         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 570     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 571         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 572     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 573         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 574     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 575         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 576     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 577         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 578     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 579         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE
 580 };
 581 
 582 /* token type table for MIME parsing */
 583 unsigned char   MimeTokenTab[256] =
 584 {
 585     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 586         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL,
 587     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 588         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 589     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 590         SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR,
 591     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 592         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR,
 593     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 594         OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 595     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 596         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM,
 597     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 598         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 599     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 600         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 601 
 602     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 603         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 604     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 605         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 606     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 607         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 608     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 609         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 610     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 611         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 612     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 613         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 614     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 615         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL,
 616     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 617         ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ONE
 618 };
 619 
 620 /* token type table: don't strip comments */
 621 unsigned char   TokTypeNoC[256] =
 622 {
 623     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 624         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM,
 625     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 626         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 627     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 628         SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM,
 629     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 630         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 631     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 632         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 633     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 634         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 635     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 636         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 637     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 638         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 639 
 640     /*  nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si   */
 641         OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
 642     /*  dle dc1 dc2 dc3 dc4 nak syn etb  can em  sub esc fs  gs  rs  us   */
 643         OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR,
 644     /*  sp  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /    */
 645         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 646     /*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?    */
 647         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 648     /*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O    */
 649         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 650     /*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _    */
 651         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 652     /*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o    */
 653         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM,
 654     /*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~   del  */
 655         ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE
 656 };
 657 
 658 
 659 #define NOCHAR          (-1)    /* signal nothing in lookahead token */
 660 
 661 char **
 662 prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab, ignore)
 663         char *addr;
 664         int delim;
 665         char pvpbuf[];
 666         int pvpbsize;
 667         char **delimptr;
 668         unsigned char *toktab;
 669         bool ignore;
 670 {
 671         register char *p;
 672         register char *q;
 673         register int c;
 674         char **avp;
 675         bool bslashmode;
 676         bool route_syntax;
 677         int cmntcnt;
 678         int anglecnt;
 679         char *tok;
 680         int state;
 681         int newstate;
 682         char *saveto = CurEnv->e_to;
 683         static char *av[MAXATOM + 1];
 684         static bool firsttime = true;
 685 
 686         if (firsttime)
 687         {
 688                 /* initialize the token type table */
 689                 char obuf[50];
 690 
 691                 firsttime = false;
 692                 if (OperatorChars == NULL)
 693                 {
 694                         if (ConfigLevel < 7)
 695                                 OperatorChars = macvalue('o', CurEnv);
 696                         if (OperatorChars == NULL)
 697                                 OperatorChars = ".:@[]";
 698                 }
 699                 expand(OperatorChars, obuf, sizeof(obuf) - sizeof(DELIMCHARS),
 700                        CurEnv);
 701                 (void) sm_strlcat(obuf, DELIMCHARS, sizeof(obuf));
 702                 for (p = obuf; *p != '\0'; p++)
 703                 {
 704                         if (IntTokenTab[*p & 0xff] == ATM)
 705                                 IntTokenTab[*p & 0xff] = OPR;
 706                         if (ExtTokenTab[*p & 0xff] == ATM)
 707                                 ExtTokenTab[*p & 0xff] = OPR;
 708                         if (TokTypeNoC[*p & 0xff] == ATM)
 709                                 TokTypeNoC[*p & 0xff] = OPR;
 710                 }
 711         }
 712         if (toktab == NULL)
 713                 toktab = ExtTokenTab;
 714 
 715         /* make sure error messages don't have garbage on them */
 716         errno = 0;
 717 
 718         q = pvpbuf;
 719         bslashmode = false;
 720         route_syntax = false;
 721         cmntcnt = 0;
 722         anglecnt = 0;
 723         avp = av;
 724         state = ATM;
 725         c = NOCHAR;
 726         p = addr;
 727         CurEnv->e_to = p;
 728         if (tTd(22, 11))
 729         {
 730                 sm_dprintf("prescan: ");
 731                 xputs(sm_debug_file(), p);
 732                 sm_dprintf("\n");
 733         }
 734 
 735         do
 736         {
 737                 /* read a token */
 738                 tok = q;
 739                 for (;;)
 740                 {
 741                         /* store away any old lookahead character */
 742                         if (c != NOCHAR && !bslashmode)
 743                         {
 744                                 /* see if there is room */
 745                                 if (q >= &pvpbuf[pvpbsize - 5])
 746                                 {
 747         addrtoolong:
 748                                         usrerr("553 5.1.1 Address too long");
 749                                         if (strlen(addr) > MAXNAME)
 750                                                 addr[MAXNAME] = '\0';
 751         returnnull:
 752                                         if (delimptr != NULL)
 753                                         {
 754                                                 if (p > addr)
 755                                                         --p;
 756                                                 *delimptr = p;
 757                                         }
 758                                         CurEnv->e_to = saveto;
 759                                         return NULL;
 760                                 }
 761 
 762                                 /* squirrel it away */
 763 #if !ALLOW_255
 764                                 if ((char) c == (char) -1 && !tTd(82, 101) &&
 765                                     !EightBitAddrOK)
 766                                         c &= 0x7f;
 767 #endif /* !ALLOW_255 */
 768                                 *q++ = c;
 769                         }
 770 
 771                         /* read a new input character */
 772                         c = (*p++) & 0x00ff;
 773                         if (c == '\0')
 774                         {
 775                                 /* diagnose and patch up bad syntax */
 776                                 if (ignore)
 777                                         break;
 778                                 else if (state == QST)
 779                                 {
 780                                         usrerr("553 Unbalanced '\"'");
 781                                         c = '"';
 782                                 }
 783                                 else if (cmntcnt > 0)
 784                                 {
 785                                         usrerr("553 Unbalanced '('");
 786                                         c = ')';
 787                                 }
 788                                 else if (anglecnt > 0)
 789                                 {
 790                                         c = '>';
 791                                         usrerr("553 Unbalanced '<'");
 792                                 }
 793                                 else
 794                                         break;
 795 
 796                                 p--;
 797                         }
 798                         else if (c == delim && cmntcnt <= 0 && state != QST)
 799                         {
 800                                 if (anglecnt <= 0)
 801                                         break;
 802 
 803                                 /* special case for better error management */
 804                                 if (delim == ',' && !route_syntax && !ignore)
 805                                 {
 806                                         usrerr("553 Unbalanced '<'");
 807                                         c = '>';
 808                                         p--;
 809                                 }
 810                         }
 811 
 812                         if (tTd(22, 101))
 813                                 sm_dprintf("c=%c, s=%d; ", c, state);
 814 
 815                         /* chew up special characters */
 816                         *q = '\0';
 817                         if (bslashmode)
 818                         {
 819                                 bslashmode = false;
 820 
 821                                 /* kludge \! for naive users */
 822                                 if (cmntcnt > 0)
 823                                 {
 824                                         c = NOCHAR;
 825                                         continue;
 826                                 }
 827                                 else if (c != '!' || state == QST)
 828                                 {
 829                                         /* see if there is room */
 830                                         if (q >= &pvpbuf[pvpbsize - 5])
 831                                                 goto addrtoolong;
 832                                         *q++ = '\\';
 833                                         continue;
 834                                 }
 835                         }
 836 
 837                         if (c == '\\')
 838                         {
 839                                 bslashmode = true;
 840                         }
 841                         else if (state == QST)
 842                         {
 843                                 /* EMPTY */
 844                                 /* do nothing, just avoid next clauses */
 845                         }
 846                         else if (c == '(' && toktab['('] == SPC)
 847                         {
 848                                 cmntcnt++;
 849                                 c = NOCHAR;
 850                         }
 851                         else if (c == ')' && toktab['('] == SPC)
 852                         {
 853                                 if (cmntcnt <= 0)
 854                                 {
 855                                         if (!ignore)
 856                                         {
 857                                                 usrerr("553 Unbalanced ')'");
 858                                                 c = NOCHAR;
 859                                         }
 860                                 }
 861                                 else
 862                                         cmntcnt--;
 863                         }
 864                         else if (cmntcnt > 0)
 865                         {
 866                                 c = NOCHAR;
 867                         }
 868                         else if (c == '<')
 869                         {
 870                                 char *ptr = p;
 871 
 872                                 anglecnt++;
 873                                 while (isascii(*ptr) && isspace(*ptr))
 874                                         ptr++;
 875                                 if (*ptr == '@')
 876                                         route_syntax = true;
 877                         }
 878                         else if (c == '>')
 879                         {
 880                                 if (anglecnt <= 0)
 881                                 {
 882                                         if (!ignore)
 883                                         {
 884                                                 usrerr("553 Unbalanced '>'");
 885                                                 c = NOCHAR;
 886                                         }
 887                                 }
 888                                 else
 889                                         anglecnt--;
 890                                 route_syntax = false;
 891                         }
 892                         else if (delim == ' ' && isascii(c) && isspace(c))
 893                                 c = ' ';
 894 
 895                         if (c == NOCHAR)
 896                                 continue;
 897 
 898                         /* see if this is end of input */
 899                         if (c == delim && anglecnt <= 0 && state != QST)
 900                                 break;
 901 
 902                         newstate = StateTab[state][toktab[c & 0xff]];
 903                         if (tTd(22, 101))
 904                                 sm_dprintf("ns=%02o\n", newstate);
 905                         state = newstate & TYPE;
 906                         if (state == ILL)
 907                         {
 908                                 if (isascii(c) && isprint(c))
 909                                         usrerr("553 Illegal character %c", c);
 910                                 else
 911                                         usrerr("553 Illegal character 0x%02x",
 912                                                c & 0x0ff);
 913                         }
 914                         if (bitset(M, newstate))
 915                                 c = NOCHAR;
 916                         if (bitset(B, newstate))
 917                                 break;
 918                 }
 919 
 920                 /* new token */
 921                 if (tok != q)
 922                 {
 923                         /* see if there is room */
 924                         if (q >= &pvpbuf[pvpbsize - 5])
 925                                 goto addrtoolong;
 926                         *q++ = '\0';
 927                         if (tTd(22, 36))
 928                         {
 929                                 sm_dprintf("tok=");
 930                                 xputs(sm_debug_file(), tok);
 931                                 sm_dprintf("\n");
 932                         }
 933                         if (avp >= &av[MAXATOM])
 934                         {
 935                                 usrerr("553 5.1.0 prescan: too many tokens");
 936                                 goto returnnull;
 937                         }
 938                         if (q - tok > MAXNAME)
 939                         {
 940                                 usrerr("553 5.1.0 prescan: token too long");
 941                                 goto returnnull;
 942                         }
 943                         *avp++ = tok;
 944                 }
 945         } while (c != '\0' && (c != delim || anglecnt > 0));
 946         *avp = NULL;
 947         if (delimptr != NULL)
 948         {
 949                 if (p > addr)
 950                         p--;
 951                 *delimptr = p;
 952         }
 953         if (tTd(22, 12))
 954         {
 955                 sm_dprintf("prescan==>");
 956                 printav(sm_debug_file(), av);
 957         }
 958         CurEnv->e_to = saveto;
 959         if (av[0] == NULL)
 960         {
 961                 if (tTd(22, 1))
 962                         sm_dprintf("prescan: null leading token\n");
 963                 return NULL;
 964         }
 965         return av;
 966 }
 967 /*
 968 **  REWRITE -- apply rewrite rules to token vector.
 969 **
 970 **      This routine is an ordered production system.  Each rewrite
 971 **      rule has a LHS (called the pattern) and a RHS (called the
 972 **      rewrite); 'rwr' points the the current rewrite rule.
 973 **
 974 **      For each rewrite rule, 'avp' points the address vector we
 975 **      are trying to match against, and 'pvp' points to the pattern.
 976 **      If pvp points to a special match value (MATCHZANY, MATCHANY,
 977 **      MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
 978 **      matched is saved away in the match vector (pointed to by 'mvp').
 979 **
 980 **      When a match between avp & pvp does not match, we try to
 981 **      back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
 982 **      we must also back out the match in mvp.  If we reach a
 983 **      MATCHANY or MATCHZANY we just extend the match and start
 984 **      over again.
 985 **
 986 **      When we finally match, we rewrite the address vector
 987 **      and try over again.
 988 **
 989 **      Parameters:
 990 **              pvp -- pointer to token vector.
 991 **              ruleset -- the ruleset to use for rewriting.
 992 **              reclevel -- recursion level (to catch loops).
 993 **              e -- the current envelope.
 994 **              maxatom -- maximum length of buffer (usually MAXATOM)
 995 **
 996 **      Returns:
 997 **              A status code.  If EX_TEMPFAIL, higher level code should
 998 **                      attempt recovery.
 999 **
1000 **      Side Effects:
1001 **              pvp is modified.
1002 */
1003 
1004 struct match
1005 {
1006         char    **match_first;          /* first token matched */
1007         char    **match_last;           /* last token matched */
1008         char    **match_pattern;        /* pointer to pattern */
1009 };
1010 
1011 int
1012 rewrite(pvp, ruleset, reclevel, e, maxatom)
1013         char **pvp;
1014         int ruleset;
1015         int reclevel;
1016         register ENVELOPE *e;
1017         int maxatom;
1018 {
1019         register char *ap;              /* address pointer */
1020         register char *rp;              /* rewrite pointer */
1021         register char *rulename;        /* ruleset name */
1022         register char *prefix;
1023         register char **avp;            /* address vector pointer */
1024         register char **rvp;            /* rewrite vector pointer */
1025         register struct match *mlp;     /* cur ptr into mlist */
1026         register struct rewrite *rwr;   /* pointer to current rewrite rule */
1027         int ruleno;                     /* current rule number */
1028         int rstat = EX_OK;              /* return status */
1029         int loopcount;
1030         struct match mlist[MAXMATCH];   /* stores match on LHS */
1031         char *npvp[MAXATOM + 1];        /* temporary space for rebuild */
1032         char buf[MAXLINE];
1033         char name[6];
1034 
1035         /*
1036         **  mlp will not exceed mlist[] because readcf enforces
1037         **      the upper limit of entries when reading rulesets.
1038         */
1039 
1040         if (ruleset < 0 || ruleset >= MAXRWSETS)
1041         {
1042                 syserr("554 5.3.5 rewrite: illegal ruleset number %d", ruleset);
1043                 return EX_CONFIG;
1044         }
1045         rulename = RuleSetNames[ruleset];
1046         if (rulename == NULL)
1047         {
1048                 (void) sm_snprintf(name, sizeof(name), "%d", ruleset);
1049                 rulename = name;
1050         }
1051         if (OpMode == MD_TEST)
1052                 prefix = "";
1053         else
1054                 prefix = "rewrite: ruleset ";
1055         if (OpMode == MD_TEST)
1056         {
1057                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1058                                      "%s%-16.16s   input:", prefix, rulename);
1059                 printav(smioout, pvp);
1060         }
1061         else if (tTd(21, 1))
1062         {
1063                 sm_dprintf("%s%-16.16s   input:", prefix, rulename);
1064                 printav(sm_debug_file(), pvp);
1065         }
1066         if (reclevel++ > MaxRuleRecursion)
1067         {
1068                 syserr("rewrite: excessive recursion (max %d), ruleset %s",
1069                         MaxRuleRecursion, rulename);
1070                 return EX_CONFIG;
1071         }
1072         if (pvp == NULL)
1073                 return EX_USAGE;
1074         if (maxatom <= 0)
1075                 return EX_USAGE;
1076 
1077         /*
1078         **  Run through the list of rewrite rules, applying
1079         **      any that match.
1080         */
1081 
1082         ruleno = 1;
1083         loopcount = 0;
1084         for (rwr = RewriteRules[ruleset]; rwr != NULL; )
1085         {
1086                 int status;
1087 
1088                 /* if already canonical, quit now */
1089                 if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
1090                         break;
1091 
1092                 if (tTd(21, 12))
1093                 {
1094                         if (tTd(21, 15))
1095                                 sm_dprintf("-----trying rule (line %d):",
1096                                        rwr->r_line);
1097                         else
1098                                 sm_dprintf("-----trying rule:");
1099                         printav(sm_debug_file(), rwr->r_lhs);
1100                 }
1101 
1102                 /* try to match on this rule */
1103                 mlp = mlist;
1104                 rvp = rwr->r_lhs;
1105                 avp = pvp;
1106                 if (++loopcount > 100)
1107                 {
1108                         syserr("554 5.3.5 Infinite loop in ruleset %s, rule %d",
1109                                 rulename, ruleno);
1110                         if (tTd(21, 1))
1111                         {
1112                                 sm_dprintf("workspace: ");
1113                                 printav(sm_debug_file(), pvp);
1114                         }
1115                         break;
1116                 }
1117 
1118                 while ((ap = *avp) != NULL || *rvp != NULL)
1119                 {
1120                         rp = *rvp;
1121                         if (tTd(21, 35))
1122                         {
1123                                 sm_dprintf("ADVANCE rp=");
1124                                 xputs(sm_debug_file(), rp);
1125                                 sm_dprintf(", ap=");
1126                                 xputs(sm_debug_file(), ap);
1127                                 sm_dprintf("\n");
1128                         }
1129                         if (rp == NULL)
1130                         {
1131                                 /* end-of-pattern before end-of-address */
1132                                 goto backup;
1133                         }
1134                         if (ap == NULL &&
1135                             (rp[0] & 0377) != MATCHZANY &&
1136                             (rp[0] & 0377) != MATCHZERO)
1137                         {
1138                                 /* end-of-input with patterns left */
1139                                 goto backup;
1140                         }
1141 
1142                         switch (rp[0] & 0377)
1143                         {
1144                           case MATCHCLASS:
1145                                 /* match any phrase in a class */
1146                                 mlp->match_pattern = rvp;
1147                                 mlp->match_first = avp;
1148         extendclass:
1149                                 ap = *avp;
1150                                 if (ap == NULL)
1151                                         goto backup;
1152                                 mlp->match_last = avp++;
1153                                 cataddr(mlp->match_first, mlp->match_last,
1154                                         buf, sizeof(buf), '\0', true);
1155                                 if (!wordinclass(buf, rp[1]))
1156                                 {
1157                                         if (tTd(21, 36))
1158                                         {
1159                                                 sm_dprintf("EXTEND  rp=");
1160                                                 xputs(sm_debug_file(), rp);
1161                                                 sm_dprintf(", ap=");
1162                                                 xputs(sm_debug_file(), ap);
1163                                                 sm_dprintf("\n");
1164                                         }
1165                                         goto extendclass;
1166                                 }
1167                                 if (tTd(21, 36))
1168                                         sm_dprintf("CLMATCH\n");
1169                                 mlp++;
1170                                 break;
1171 
1172                           case MATCHNCLASS:
1173                                 /* match any token not in a class */
1174                                 if (wordinclass(ap, rp[1]))
1175                                         goto backup;
1176 
1177                                 /* FALLTHROUGH */
1178 
1179                           case MATCHONE:
1180                           case MATCHANY:
1181                                 /* match exactly one token */
1182                                 mlp->match_pattern = rvp;
1183                                 mlp->match_first = avp;
1184                                 mlp->match_last = avp++;
1185                                 mlp++;
1186                                 break;
1187 
1188                           case MATCHZANY:
1189                                 /* match zero or more tokens */
1190                                 mlp->match_pattern = rvp;
1191                                 mlp->match_first = avp;
1192                                 mlp->match_last = avp - 1;
1193                                 mlp++;
1194                                 break;
1195 
1196                           case MATCHZERO:
1197                                 /* match zero tokens */
1198                                 break;
1199 
1200                           case MACRODEXPAND:
1201                                 /*
1202                                 **  Match against run-time macro.
1203                                 **  This algorithm is broken for the
1204                                 **  general case (no recursive macros,
1205                                 **  improper tokenization) but should
1206                                 **  work for the usual cases.
1207                                 */
1208 
1209                                 ap = macvalue(rp[1], e);
1210                                 mlp->match_first = avp;
1211                                 if (tTd(21, 2))
1212                                         sm_dprintf("rewrite: LHS $&{%s} => \"%s\"\n",
1213                                                 macname(rp[1]),
1214                                                 ap == NULL ? "(NULL)" : ap);
1215 
1216                                 if (ap == NULL)
1217                                         break;
1218                                 while (*ap != '\0')
1219                                 {
1220                                         if (*avp == NULL ||
1221                                             sm_strncasecmp(ap, *avp,
1222                                                            strlen(*avp)) != 0)
1223                                         {
1224                                                 /* no match */
1225                                                 avp = mlp->match_first;
1226                                                 goto backup;
1227                                         }
1228                                         ap += strlen(*avp++);
1229                                 }
1230 
1231                                 /* match */
1232                                 break;
1233 
1234                           default:
1235                                 /* must have exact match */
1236                                 if (sm_strcasecmp(rp, ap))
1237                                         goto backup;
1238                                 avp++;
1239                                 break;
1240                         }
1241 
1242                         /* successful match on this token */
1243                         rvp++;
1244                         continue;
1245 
1246           backup:
1247                         /* match failed -- back up */
1248                         while (--mlp >= mlist)
1249                         {
1250                                 rvp = mlp->match_pattern;
1251                                 rp = *rvp;
1252                                 avp = mlp->match_last + 1;
1253                                 ap = *avp;
1254 
1255                                 if (tTd(21, 36))
1256                                 {
1257                                         sm_dprintf("BACKUP  rp=");
1258                                         xputs(sm_debug_file(), rp);
1259                                         sm_dprintf(", ap=");
1260                                         xputs(sm_debug_file(), ap);
1261                                         sm_dprintf("\n");
1262                                 }
1263 
1264                                 if (ap == NULL)
1265                                 {
1266                                         /* run off the end -- back up again */
1267                                         continue;
1268                                 }
1269 
1270                                 if ((rp[0] & 0377) == MATCHANY ||
1271                                     (rp[0] & 0377) == MATCHZANY)
1272                                 {
1273                                         /* extend binding and continue */
1274                                         mlp->match_last = avp++;
1275                                         rvp++;
1276                                         mlp++;
1277                                         break;
1278                                 }
1279                                 if ((rp[0] & 0377) == MATCHCLASS)
1280                                 {
1281                                         /* extend binding and try again */
1282                                         mlp->match_last = avp;
1283                                         goto extendclass;
1284                                 }
1285                         }
1286 
1287                         if (mlp < mlist)
1288                         {
1289                                 /* total failure to match */
1290                                 break;
1291                         }
1292                 }
1293 
1294                 /*
1295                 **  See if we successfully matched
1296                 */
1297 
1298                 if (mlp < mlist || *rvp != NULL)
1299                 {
1300                         if (tTd(21, 10))
1301                                 sm_dprintf("----- rule fails\n");
1302                         rwr = rwr->r_next;
1303                         ruleno++;
1304                         loopcount = 0;
1305                         continue;
1306                 }
1307 
1308                 rvp = rwr->r_rhs;
1309                 if (tTd(21, 12))
1310                 {
1311                         sm_dprintf("-----rule matches:");
1312                         printav(sm_debug_file(), rvp);
1313                 }
1314 
1315                 rp = *rvp;
1316                 if (rp != NULL)
1317                 {
1318                         if ((rp[0] & 0377) == CANONUSER)
1319                         {
1320                                 rvp++;
1321                                 rwr = rwr->r_next;
1322                                 ruleno++;
1323                                 loopcount = 0;
1324                         }
1325                         else if ((rp[0] & 0377) == CANONHOST)
1326                         {
1327                                 rvp++;
1328                                 rwr = NULL;
1329                         }
1330                 }
1331 
1332                 /* substitute */
1333                 for (avp = npvp; *rvp != NULL; rvp++)
1334                 {
1335                         register struct match *m;
1336                         register char **pp;
1337 
1338                         rp = *rvp;
1339                         if ((rp[0] & 0377) == MATCHREPL)
1340                         {
1341                                 /* substitute from LHS */
1342                                 m = &mlist[rp[1] - '1'];
1343                                 if (m < mlist || m >= mlp)
1344                                 {
1345                                         syserr("554 5.3.5 rewrite: ruleset %s: replacement $%c out of bounds",
1346                                                 rulename, rp[1]);
1347                                         return EX_CONFIG;
1348                                 }
1349                                 if (tTd(21, 15))
1350                                 {
1351                                         sm_dprintf("$%c:", rp[1]);
1352                                         pp = m->match_first;
1353                                         while (pp <= m->match_last)
1354                                         {
1355                                                 sm_dprintf(" %p=\"", *pp);
1356                                                 sm_dflush();
1357                                                 sm_dprintf("%s\"", *pp++);
1358                                         }
1359                                         sm_dprintf("\n");
1360                                 }
1361                                 pp = m->match_first;
1362                                 while (pp <= m->match_last)
1363                                 {
1364                                         if (avp >= &npvp[maxatom])
1365                                                 goto toolong;
1366                                         *avp++ = *pp++;
1367                                 }
1368                         }
1369                         else
1370                         {
1371                                 /* some sort of replacement */
1372                                 if (avp >= &npvp[maxatom])
1373                                 {
1374         toolong:
1375                                         syserr("554 5.3.0 rewrite: expansion too long");
1376                                         if (LogLevel > 9)
1377                                                 sm_syslog(LOG_ERR, e->e_id,
1378                                                         "rewrite: expansion too long, ruleset=%s, ruleno=%d",
1379                                                         rulename, ruleno);
1380                                         return EX_DATAERR;
1381                                 }
1382                                 if ((rp[0] & 0377) != MACRODEXPAND)
1383                                 {
1384                                         /* vanilla replacement from RHS */
1385                                         *avp++ = rp;
1386                                 }
1387                                 else
1388                                 {
1389                                         /* $&{x} replacement */
1390                                         char *mval = macvalue(rp[1], e);
1391                                         char **xpvp;
1392                                         size_t trsize = 0;
1393                                         static size_t pvpb1_size = 0;
1394                                         static char **pvpb1 = NULL;
1395                                         char pvpbuf[PSBUFSIZE];
1396 
1397                                         if (tTd(21, 2))
1398                                                 sm_dprintf("rewrite: RHS $&{%s} => \"%s\"\n",
1399                                                         macname(rp[1]),
1400                                                         mval == NULL ? "(NULL)" : mval);
1401                                         if (mval == NULL || *mval == '\0')
1402                                                 continue;
1403 
1404                                         /* save the remainder of the input */
1405                                         for (xpvp = pvp; *xpvp != NULL; xpvp++)
1406                                                 trsize += sizeof(*xpvp);
1407                                         if (trsize > pvpb1_size)
1408                                         {
1409                                                 if (pvpb1 != NULL)
1410                                                         sm_free(pvpb1);
1411                                                 pvpb1 = (char **)
1412                                                         sm_pmalloc_x(trsize);
1413                                                 pvpb1_size = trsize;
1414                                         }
1415 
1416                                         memmove((char *) pvpb1,
1417                                                 (char *) pvp,
1418                                                 trsize);
1419 
1420                                         /* scan the new replacement */
1421                                         xpvp = prescan(mval, '\0', pvpbuf,
1422                                                        sizeof(pvpbuf), NULL,
1423                                                        NULL, false);
1424                                         if (xpvp == NULL)
1425                                         {
1426                                                 /* prescan pre-printed error */
1427                                                 return EX_DATAERR;
1428                                         }
1429 
1430                                         /* insert it into the output stream */
1431                                         while (*xpvp != NULL)
1432                                         {
1433                                                 if (tTd(21, 19))
1434                                                         sm_dprintf(" ... %s\n",
1435                                                                 *xpvp);
1436                                                 *avp++ = sm_rpool_strdup_x(
1437                                                         e->e_rpool, *xpvp);
1438                                                 if (avp >= &npvp[maxatom])
1439                                                         goto toolong;
1440                                                 xpvp++;
1441                                         }
1442                                         if (tTd(21, 19))
1443                                                 sm_dprintf(" ... DONE\n");
1444 
1445                                         /* restore the old trailing input */
1446                                         memmove((char *) pvp,
1447                                                 (char *) pvpb1,
1448                                                 trsize);
1449                                 }
1450                         }
1451                 }
1452                 *avp++ = NULL;
1453 
1454                 /*
1455                 **  Check for any hostname/keyword lookups.
1456                 */
1457 
1458                 for (rvp = npvp; *rvp != NULL; rvp++)
1459                 {
1460                         char **hbrvp;
1461                         char **xpvp;
1462                         size_t trsize;
1463                         char *replac;
1464                         int endtoken;
1465                         bool external;
1466                         STAB *map;
1467                         char *mapname;
1468                         char **key_rvp;
1469                         char **arg_rvp;
1470                         char **default_rvp;
1471                         char cbuf[MAXKEY];
1472                         char *pvpb1[MAXATOM + 1];
1473                         char *argvect[MAX_MAP_ARGS];
1474                         char pvpbuf[PSBUFSIZE];
1475                         char *nullpvp[1];
1476 
1477                         hbrvp = rvp;
1478                         if ((rvp[0][0] & 0377) == HOSTBEGIN)
1479                         {
1480                                 endtoken = HOSTEND;
1481                                 mapname = "host";
1482                         }
1483                         else if ((rvp[0][0] & 0377) == LOOKUPBEGIN)
1484                         {
1485                                 endtoken = LOOKUPEND;
1486                                 mapname = *++rvp;
1487                                 if (mapname == NULL)
1488                                 {
1489                                         syserr("554 5.3.0 rewrite: missing mapname");
1490                                         /* NOTREACHED */
1491                                         SM_ASSERT(0);
1492                                 }
1493                         }
1494                         else
1495                                 continue;
1496 
1497                         /*
1498                         **  Got a hostname/keyword lookup.
1499                         **
1500                         **      This could be optimized fairly easily.
1501                         */
1502 
1503                         map = stab(mapname, ST_MAP, ST_FIND);
1504                         if (map == NULL)
1505                                 syserr("554 5.3.0 rewrite: map %s not found",
1506                                         mapname);
1507 
1508                         /* extract the match part */
1509                         key_rvp = ++rvp;
1510                         if (key_rvp == NULL)
1511                         {
1512                                 syserr("554 5.3.0 rewrite: missing key for map %s",
1513                                         mapname);
1514                                 /* NOTREACHED */
1515                                 SM_ASSERT(0);
1516                         }
1517                         default_rvp = NULL;
1518                         arg_rvp = argvect;
1519                         xpvp = NULL;
1520                         replac = pvpbuf;
1521                         while (*rvp != NULL && ((rvp[0][0] & 0377) != endtoken))
1522                         {
1523                                 int nodetype = rvp[0][0] & 0377;
1524 
1525                                 if (nodetype != CANONHOST &&
1526                                     nodetype != CANONUSER)
1527                                 {
1528                                         rvp++;
1529                                         continue;
1530                                 }
1531 
1532                                 *rvp++ = NULL;
1533 
1534                                 if (xpvp != NULL)
1535                                 {
1536                                         cataddr(xpvp, NULL, replac,
1537                                                 &pvpbuf[sizeof(pvpbuf)] - replac,
1538                                                 '\0', false);
1539                                         if (arg_rvp <
1540                                             &argvect[MAX_MAP_ARGS - 1])
1541                                                 *++arg_rvp = replac;
1542                                         replac += strlen(replac) + 1;
1543                                         xpvp = NULL;
1544                                 }
1545                                 switch (nodetype)
1546                                 {
1547                                   case CANONHOST:
1548                                         xpvp = rvp;
1549                                         break;
1550 
1551                                   case CANONUSER:
1552                                         default_rvp = rvp;
1553                                         break;
1554                                 }
1555                         }
1556                         if (*rvp != NULL)
1557                                 *rvp++ = NULL;
1558                         if (xpvp != NULL)
1559                         {
1560                                 cataddr(xpvp, NULL, replac,
1561                                         &pvpbuf[sizeof(pvpbuf)] - replac,
1562                                         '\0', false);
1563                                 if (arg_rvp < &argvect[MAX_MAP_ARGS - 1])
1564                                         *++arg_rvp = replac;
1565                         }
1566                         if (arg_rvp >= &argvect[MAX_MAP_ARGS - 1])
1567                                 argvect[MAX_MAP_ARGS - 1] = NULL;
1568                         else
1569                                 *++arg_rvp = NULL;
1570 
1571                         /* save the remainder of the input string */
1572                         trsize = (avp - rvp + 1) * sizeof(*rvp);
1573                         memmove((char *) pvpb1, (char *) rvp, trsize);
1574 
1575                         /* look it up */
1576                         cataddr(key_rvp, NULL, cbuf, sizeof(cbuf),
1577                                 map == NULL ? '\0' : map->s_map.map_spacesub,
1578                                 true);
1579                         argvect[0] = cbuf;
1580                         replac = map_lookup(map, cbuf, argvect, &rstat, e);
1581                         external = replac != NULL;
1582 
1583                         /* if no replacement, use default */
1584                         if (replac == NULL && default_rvp != NULL)
1585                         {
1586                                 /* create the default */
1587                                 cataddr(default_rvp, NULL, cbuf, sizeof(cbuf),
1588                                         '\0', false);
1589                                 replac = cbuf;
1590                         }
1591 
1592                         if (replac == NULL)
1593                         {
1594                                 xpvp = key_rvp;
1595                         }
1596                         else if (*replac == '\0')
1597                         {
1598                                 /* null replacement */
1599                                 nullpvp[0] = NULL;
1600                                 xpvp = nullpvp;
1601                         }
1602                         else
1603                         {
1604                                 /* scan the new replacement */
1605                                 xpvp = prescan(replac, '\0', pvpbuf,
1606                                                sizeof(pvpbuf), NULL,
1607                                                external ? NULL : IntTokenTab,
1608                                                false);
1609                                 if (xpvp == NULL)
1610                                 {
1611                                         /* prescan already printed error */
1612                                         return EX_DATAERR;
1613                                 }
1614                         }
1615 
1616                         /* append it to the token list */
1617                         for (avp = hbrvp; *xpvp != NULL; xpvp++)
1618                         {
1619                                 *avp++ = sm_rpool_strdup_x(e->e_rpool, *xpvp);
1620                                 if (avp >= &npvp[maxatom])
1621                                         goto toolong;
1622                         }
1623 
1624                         /* restore the old trailing information */
1625                         rvp = avp - 1;
1626                         for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
1627                                 if (avp >= &npvp[maxatom])
1628                                         goto toolong;
1629                 }
1630 
1631                 /*
1632                 **  Check for subroutine calls.
1633                 */
1634 
1635                 status = callsubr(npvp, reclevel, e);
1636                 if (rstat == EX_OK || status == EX_TEMPFAIL)
1637                         rstat = status;
1638 
1639                 /* copy vector back into original space. */
1640                 for (avp = npvp; *avp++ != NULL;)
1641                         continue;
1642                 memmove((char *) pvp, (char *) npvp,
1643                       (int) (avp - npvp) * sizeof(*avp));
1644 
1645                 if (tTd(21, 4))
1646                 {
1647                         sm_dprintf("rewritten as:");
1648                         printav(sm_debug_file(), pvp);
1649                 }
1650         }
1651 
1652         if (OpMode == MD_TEST)
1653         {
1654                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1655                                      "%s%-16.16s returns:", prefix, rulename);
1656                 printav(smioout, pvp);
1657         }
1658         else if (tTd(21, 1))
1659         {
1660                 sm_dprintf("%s%-16.16s returns:", prefix, rulename);
1661                 printav(sm_debug_file(), pvp);
1662         }
1663         return rstat;
1664 }
1665 /*
1666 **  CALLSUBR -- call subroutines in rewrite vector
1667 **
1668 **      Parameters:
1669 **              pvp -- pointer to token vector.
1670 **              reclevel -- the current recursion level.
1671 **              e -- the current envelope.
1672 **
1673 **      Returns:
1674 **              The status from the subroutine call.
1675 **
1676 **      Side Effects:
1677 **              pvp is modified.
1678 */
1679 
1680 static int
1681 callsubr(pvp, reclevel, e)
1682         char **pvp;
1683         int reclevel;
1684         ENVELOPE *e;
1685 {
1686         char **avp;
1687         register int i;
1688         int subr, j;
1689         int nsubr;
1690         int status;
1691         int rstat = EX_OK;
1692 #define MAX_SUBR        16
1693         int subrnumber[MAX_SUBR];
1694         int subrindex[MAX_SUBR];
1695 
1696         nsubr = 0;
1697 
1698         /*
1699         **  Look for subroutine calls in pvp, collect them into subr*[]
1700         **  We will perform the calls in the next loop, because we will
1701         **  call the "last" subroutine first to avoid recursive calls
1702         **  and too much copying.
1703         */
1704 
1705         for (avp = pvp, j = 0; *avp != NULL; avp++, j++)
1706         {
1707                 if ((avp[0][0] & 0377) == CALLSUBR && avp[1] != NULL)
1708                 {
1709                         stripquotes(avp[1]);
1710                         subr = strtorwset(avp[1], NULL, ST_FIND);
1711                         if (subr < 0)
1712                         {
1713                                 syserr("554 5.3.5 Unknown ruleset %s", avp[1]);
1714                                 return EX_CONFIG;
1715                         }
1716 
1717                         /*
1718                         **  XXX instead of doing this we could optimize
1719                         **  the rules after reading them: just remove
1720                         **  calls to empty rulesets
1721                         */
1722 
1723                         /* subroutine is an empty ruleset?  don't call it */
1724                         if (RewriteRules[subr] == NULL)
1725                         {
1726                                 if (tTd(21, 3))
1727                                         sm_dprintf("-----skip subr %s (%d)\n",
1728                                                 avp[1], subr);
1729                                 for (i = 2; avp[i] != NULL; i++)
1730                                         avp[i - 2] = avp[i];
1731                                 avp[i - 2] = NULL;
1732                                 continue;
1733                         }
1734                         if (++nsubr >= MAX_SUBR)
1735                         {
1736                                 syserr("554 5.3.0 Too many subroutine calls (%d max)",
1737                                         MAX_SUBR);
1738                                 return EX_CONFIG;
1739                         }
1740                         subrnumber[nsubr] = subr;
1741                         subrindex[nsubr] = j;
1742                 }
1743         }
1744 
1745         /*
1746         **  Perform the actual subroutines calls, "last" one first, i.e.,
1747         **  go from the right to the left through all calls,
1748         **  do the rewriting in place.
1749         */
1750 
1751         for (; nsubr > 0; nsubr--)
1752         {
1753                 subr = subrnumber[nsubr];
1754                 avp = pvp + subrindex[nsubr];
1755 
1756                 /* remove the subroutine call and name */
1757                 for (i = 2; avp[i] != NULL; i++)
1758                         avp[i - 2] = avp[i];
1759                 avp[i - 2] = NULL;
1760 
1761                 /*
1762                 **  Now we need to call the ruleset specified for
1763                 **  the subroutine. We can do this in place since
1764                 **  we call the "last" subroutine first.
1765                 */
1766 
1767                 status = rewrite(avp, subr, reclevel, e,
1768                                 MAXATOM - subrindex[nsubr]);
1769                 if (status != EX_OK && status != EX_TEMPFAIL)
1770                         return status;
1771                 if (rstat == EX_OK || status == EX_TEMPFAIL)
1772                         rstat = status;
1773         }
1774         return rstat;
1775 }
1776 /*
1777 **  MAP_LOOKUP -- do lookup in map
1778 **
1779 **      Parameters:
1780 **              smap -- the map to use for the lookup.
1781 **              key -- the key to look up.
1782 **              argvect -- arguments to pass to the map lookup.
1783 **              pstat -- a pointer to an integer in which to store the
1784 **                      status from the lookup.
1785 **              e -- the current envelope.
1786 **
1787 **      Returns:
1788 **              The result of the lookup.
1789 **              NULL -- if there was no data for the given key.
1790 */
1791 
1792 static char *
1793 map_lookup(smap, key, argvect, pstat, e)
1794         STAB *smap;
1795         char key[];
1796         char **argvect;
1797         int *pstat;
1798         ENVELOPE *e;
1799 {
1800         auto int status = EX_OK;
1801         MAP *map;
1802         char *replac;
1803 
1804         if (smap == NULL)
1805                 return NULL;
1806 
1807         map = &smap->s_map;
1808         DYNOPENMAP(map);
1809 
1810         if (e->e_sendmode == SM_DEFER &&
1811             bitset(MF_DEFER, map->map_mflags))
1812         {
1813                 /* don't do any map lookups */
1814                 if (tTd(60, 1))
1815                         sm_dprintf("map_lookup(%s, %s) => DEFERRED\n",
1816                                 smap->s_name, key);
1817                 *pstat = EX_TEMPFAIL;
1818                 return NULL;
1819         }
1820 
1821         if (!bitset(MF_KEEPQUOTES, map->map_mflags))
1822                 stripquotes(key);
1823 
1824         if (tTd(60, 1))
1825         {
1826                 sm_dprintf("map_lookup(%s, ", smap->s_name);
1827                 xputs(sm_debug_file(), key);
1828                 if (tTd(60, 5))
1829                 {
1830                         int i;
1831 
1832                         for (i = 0; argvect[i] != NULL; i++)
1833                                 sm_dprintf(", %%%d=%s", i, argvect[i]);
1834                 }
1835                 sm_dprintf(") => ");
1836         }
1837         replac = (*map->map_class->map_lookup)(map, key, argvect, &status);
1838         if (tTd(60, 1))
1839                 sm_dprintf("%s (%d)\n",
1840                         replac != NULL ? replac : "NOT FOUND",
1841                         status);
1842 
1843         /* should recover if status == EX_TEMPFAIL */
1844         if (status == EX_TEMPFAIL && !bitset(MF_NODEFER, map->map_mflags))
1845         {
1846                 *pstat = EX_TEMPFAIL;
1847                 if (tTd(60, 1))
1848                         sm_dprintf("map_lookup(%s, %s) tempfail: errno=%d\n",
1849                                 smap->s_name, key, errno);
1850                 if (e->e_message == NULL)
1851                 {
1852                         char mbuf[320];
1853 
1854                         (void) sm_snprintf(mbuf, sizeof(mbuf),
1855                                 "%.80s map: lookup (%s): deferred",
1856                                 smap->s_name,
1857                                 shortenstring(key, MAXSHORTSTR));
1858                         e->e_message = sm_rpool_strdup_x(e->e_rpool, mbuf);
1859                 }
1860         }
1861         if (status == EX_TEMPFAIL && map->map_tapp != NULL)
1862         {
1863                 size_t i = strlen(key) + strlen(map->map_tapp) + 1;
1864                 static char *rwbuf = NULL;
1865                 static size_t rwbuflen = 0;
1866 
1867                 if (i > rwbuflen)
1868                 {
1869                         if (rwbuf != NULL)
1870                                 sm_free(rwbuf);
1871                         rwbuflen = i;
1872                         rwbuf = (char *) sm_pmalloc_x(rwbuflen);
1873                 }
1874                 (void) sm_strlcpyn(rwbuf, rwbuflen, 2, key, map->map_tapp);
1875                 if (tTd(60, 4))
1876                         sm_dprintf("map_lookup tempfail: returning \"%s\"\n",
1877                                 rwbuf);
1878                 return rwbuf;
1879         }
1880         return replac;
1881 }
1882 /*
1883 **  INITERRMAILERS -- initialize error and discard mailers
1884 **
1885 **      Parameters:
1886 **              none.
1887 **
1888 **      Returns:
1889 **              none.
1890 **
1891 **      Side Effects:
1892 **              initializes error and discard mailers.
1893 */
1894 
1895 static MAILER discardmailer;
1896 static MAILER errormailer;
1897 static char *discardargv[] = { "DISCARD", NULL };
1898 static char *errorargv[] = { "ERROR", NULL };
1899 
1900 void
1901 initerrmailers()
1902 {
1903         if (discardmailer.m_name == NULL)
1904         {
1905                 /* initialize the discard mailer */
1906                 discardmailer.m_name = "*discard*";
1907                 discardmailer.m_mailer = "DISCARD";
1908                 discardmailer.m_argv = discardargv;
1909         }
1910         if (errormailer.m_name == NULL)
1911         {
1912                 /* initialize the bogus mailer */
1913                 errormailer.m_name = "*error*";
1914                 errormailer.m_mailer = "ERROR";
1915                 errormailer.m_argv = errorargv;
1916         }
1917 }
1918 /*
1919 **  BUILDADDR -- build address from token vector.
1920 **
1921 **      Parameters:
1922 **              tv -- token vector.
1923 **              a -- pointer to address descriptor to fill.
1924 **                      If NULL, one will be allocated.
1925 **              flags -- info regarding whether this is a sender or
1926 **                      a recipient.
1927 **              e -- the current envelope.
1928 **
1929 **      Returns:
1930 **              NULL if there was an error.
1931 **              'a' otherwise.
1932 **
1933 **      Side Effects:
1934 **              fills in 'a'
1935 */
1936 
1937 static struct errcodes
1938 {
1939         char    *ec_name;               /* name of error code */
1940         int     ec_code;                /* numeric code */
1941 } ErrorCodes[] =
1942 {
1943         { "usage",              EX_USAGE        },
1944         { "nouser",             EX_NOUSER       },
1945         { "nohost",             EX_NOHOST       },
1946         { "unavailable",        EX_UNAVAILABLE  },
1947         { "software",           EX_SOFTWARE     },
1948         { "tempfail",           EX_TEMPFAIL     },
1949         { "protocol",           EX_PROTOCOL     },
1950         { "config",             EX_CONFIG       },
1951         { NULL,                 EX_UNAVAILABLE  }
1952 };
1953 
1954 static ADDRESS *
1955 buildaddr(tv, a, flags, e)
1956         register char **tv;
1957         register ADDRESS *a;
1958         int flags;
1959         register ENVELOPE *e;
1960 {
1961         bool tempfail = false;
1962         int maxatom;
1963         struct mailer **mp;
1964         register struct mailer *m;
1965         register char *p;
1966         char *mname;
1967         char **hostp;
1968         char hbuf[MAXNAME + 1];
1969         static char ubuf[MAXNAME + 2];
1970 
1971         if (tTd(24, 5))
1972         {
1973                 sm_dprintf("buildaddr, flags=%x, tv=", flags);
1974                 printav(sm_debug_file(), tv);
1975         }
1976 
1977         maxatom = MAXATOM;
1978         if (a == NULL)
1979                 a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
1980         memset((char *) a, '\0', sizeof(*a));
1981         hbuf[0] = '\0';
1982 
1983         /* set up default error return flags */
1984         a->q_flags |= DefaultNotify;
1985 
1986         /* figure out what net/mailer to use */
1987         if (*tv == NULL || (**tv & 0377) != CANONNET)
1988         {
1989                 syserr("554 5.3.5 buildaddr: no mailer in parsed address");
1990 badaddr:
1991                 /*
1992                 **  ExitStat may have been set by an earlier map open
1993                 **  failure (to a permanent error (EX_OSERR) in syserr())
1994                 **  so we also need to check if this particular $#error
1995                 **  return wanted a 4XX failure.
1996                 **
1997                 **  XXX the real fix is probably to set ExitStat correctly,
1998                 **  i.e., to EX_TEMPFAIL if the map open is just a temporary
1999                 **  error.
2000                 */
2001 
2002                 if (ExitStat == EX_TEMPFAIL || tempfail)
2003                         a->q_state = QS_QUEUEUP;
2004                 else
2005                 {
2006                         a->q_state = QS_BADADDR;
2007                         a->q_mailer = &errormailer;
2008                 }
2009                 return a;
2010         }
2011         mname = *++tv;
2012         --maxatom;
2013 
2014         /* extract host and user portions */
2015         if (*++tv != NULL && (**tv & 0377) == CANONHOST)
2016         {
2017                 hostp = ++tv;
2018                 --maxatom;
2019         }
2020         else
2021                 hostp = NULL;
2022         --maxatom;
2023         while (*tv != NULL && (**tv & 0377) != CANONUSER)
2024         {
2025                 tv++;
2026                 --maxatom;
2027         }
2028         if (*tv == NULL)
2029         {
2030                 syserr("554 5.3.5 buildaddr: no user");
2031                 goto badaddr;
2032         }
2033         if (tv == hostp)
2034                 hostp = NULL;
2035         else if (hostp != NULL)
2036                 cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', false);
2037         cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false);
2038         --maxatom;
2039 
2040         /* save away the host name */
2041         if (sm_strcasecmp(mname, "error") == 0)
2042         {
2043                 /* Set up triplet for use by -bv */
2044                 a->q_mailer = &errormailer;
2045                 a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
2046                 /* XXX wrong place? */
2047 
2048                 if (hostp != NULL)
2049                 {
2050                         register struct errcodes *ep;
2051 
2052                         a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
2053                         if (strchr(hbuf, '.') != NULL)
2054                         {
2055                                 a->q_status = sm_rpool_strdup_x(e->e_rpool,
2056                                                                 hbuf);
2057                                 setstat(dsntoexitstat(hbuf));
2058                         }
2059                         else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
2060                         {
2061                                 setstat(atoi(hbuf));
2062                         }
2063                         else
2064                         {
2065                                 for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
2066                                         if (sm_strcasecmp(ep->ec_name, hbuf) == 0)
2067                                                 break;
2068                                 setstat(ep->ec_code);
2069                         }
2070                 }
2071                 else
2072                 {
2073                         a->q_host = NULL;
2074                         setstat(EX_UNAVAILABLE);
2075                 }
2076                 stripquotes(ubuf);
2077                 if (ISSMTPCODE(ubuf) && ubuf[3] == ' ')
2078                 {
2079                         char fmt[16];
2080                         int off;
2081 
2082                         if ((off = isenhsc(ubuf + 4, ' ')) > 0)
2083                         {
2084                                 ubuf[off + 4] = '\0';
2085                                 off += 5;
2086                         }
2087                         else
2088                         {
2089                                 off = 4;
2090                                 ubuf[3] = '\0';
2091                         }
2092                         (void) sm_strlcpyn(fmt, sizeof(fmt), 2, ubuf, " %s");
2093                         if (off > 4)
2094                                 usrerr(fmt, ubuf + off);
2095                         else if (isenhsc(hbuf, '\0') > 0)
2096                                 usrerrenh(hbuf, fmt, ubuf + off);
2097                         else
2098                                 usrerr(fmt, ubuf + off);
2099                         /* XXX ubuf[off - 1] = ' '; */
2100                         if (ubuf[0] == '4')
2101                                 tempfail = true;
2102                 }
2103                 else
2104                 {
2105                         usrerr("553 5.3.0 %s", ubuf);
2106                 }
2107                 goto badaddr;
2108         }
2109 
2110         for (mp = Mailer; (m = *mp++) != NULL; )
2111         {
2112                 if (sm_strcasecmp(m->m_name, mname) == 0)
2113                         break;
2114         }
2115         if (m == NULL)
2116         {
2117                 syserr("554 5.3.5 buildaddr: unknown mailer %s", mname);
2118                 goto badaddr;
2119         }
2120         a->q_mailer = m;
2121 
2122         /* figure out what host (if any) */
2123         if (hostp == NULL)
2124         {
2125                 if (!bitnset(M_LOCALMAILER, m->m_flags))
2126                 {
2127                         syserr("554 5.3.5 buildaddr: no host");
2128                         goto badaddr;
2129                 }
2130                 a->q_host = NULL;
2131         }
2132         else
2133                 a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf);
2134 
2135         /* figure out the user */
2136         p = ubuf;
2137         if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
2138         {
2139                 p++;
2140                 tv++;
2141                 --maxatom;
2142                 a->q_flags |= QNOTREMOTE;
2143         }
2144 
2145         /* do special mapping for local mailer */
2146         if (*p == '"')
2147                 p++;
2148         if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
2149                 a->q_mailer = m = ProgMailer;
2150         else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
2151                 a->q_mailer = m = FileMailer;
2152         else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
2153         {
2154                 /* may be :include: */
2155                 stripquotes(ubuf);
2156                 if (sm_strncasecmp(ubuf, ":include:", 9) == 0)
2157                 {
2158                         /* if :include:, don't need further rewriting */
2159                         a->q_mailer = m = InclMailer;
2160                         a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]);
2161                         return a;
2162                 }
2163         }
2164 
2165         /* rewrite according recipient mailer rewriting rules */
2166         macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
2167 
2168         if (ConfigLevel >= 10 ||
2169             !bitset(RF_SENDERADDR|RF_HEADERADDR, flags))
2170         {
2171                 /* sender addresses done later */
2172                 (void) rewrite(tv, 2, 0, e, maxatom);
2173                 if (m->m_re_rwset > 0)
2174                        (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom);
2175         }
2176         (void) rewrite(tv, 4, 0, e, maxatom);
2177 
2178         /* save the result for the command line/RCPT argument */
2179         cataddr(tv, NULL, ubuf, sizeof(ubuf), '\0', true);
2180         a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf);
2181 
2182         /*
2183         **  Do mapping to lower case as requested by mailer
2184         */
2185 
2186         if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
2187                 makelower(a->q_host);
2188         if (!bitnset(M_USR_UPPER, m->m_flags))
2189                 makelower(a->q_user);
2190 
2191         if (tTd(24, 6))
2192         {
2193                 sm_dprintf("buildaddr => ");
2194                 printaddr(sm_debug_file(), a, false);
2195         }
2196         return a;
2197 }
2198 
2199 /*
2200 **  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
2201 **
2202 **      Parameters:
2203 **              pvp -- parameter vector to rebuild.
2204 **              evp -- last parameter to include.  Can be NULL to
2205 **                      use entire pvp.
2206 **              buf -- buffer to build the string into.
2207 **              sz -- size of buf.
2208 **              spacesub -- the space separator character; if '\0',
2209 **                      use SpaceSub.
2210 **              external -- convert to external form?
2211 **                      (no metacharacters; METAQUOTEs removed, see below)
2212 **
2213 **      Returns:
2214 **              none.
2215 **
2216 **      Side Effects:
2217 **              Destroys buf.
2218 **
2219 **      Notes:
2220 **      There are two formats for strings: internal and external.
2221 **      The external format is just an eight-bit clean string (no
2222 **      null bytes, everything else OK).  The internal format can
2223 **      include sendmail metacharacters.  The special character
2224 **      METAQUOTE essentially quotes the character following, stripping
2225 **      it of all special semantics.
2226 **
2227 **      The cataddr routine needs to be aware of whether it is producing
2228 **      an internal or external form as output (it only takes internal
2229 **      form as input).
2230 **
2231 **      The parseaddr routine has a similar issue on input, but that
2232 **      is flagged on the basis of which token table is passed in.
2233 */
2234 
2235 void
2236 cataddr(pvp, evp, buf, sz, spacesub, external)
2237         char **pvp;
2238         char **evp;
2239         char *buf;
2240         register int sz;
2241         int spacesub;
2242         bool external;
2243 {
2244         bool oatomtok, natomtok;
2245         char *p;
2246 
2247         oatomtok = natomtok = false;
2248         if (tTd(59, 14))
2249         {
2250                 sm_dprintf("cataddr(%d) <==", external);
2251                 printav(sm_debug_file(), pvp);
2252         }
2253 
2254         if (sz <= 0)
2255                 return;
2256 
2257         if (spacesub == '\0')
2258                 spacesub = SpaceSub;
2259 
2260         if (pvp == NULL)
2261         {
2262                 *buf = '\0';
2263                 return;
2264         }
2265         p = buf;
2266         sz -= 2;
2267         while (*pvp != NULL && sz > 0)
2268         {
2269                 char *q;
2270 
2271                 natomtok = (IntTokenTab[**pvp & 0xff] == ATM);
2272                 if (oatomtok && natomtok)
2273                 {
2274                         *p++ = spacesub;
2275                         if (--sz <= 0)
2276                                 break;
2277                 }
2278                 for (q = *pvp; *q != '\0'; )
2279                 {
2280                         int c;
2281 
2282                         if (--sz <= 0)
2283                                 break;
2284                         *p++ = c = *q++;
2285 
2286                         /*
2287                         **  If the current character (c) is METAQUOTE and we
2288                         **  want the "external" form and the next character
2289                         **  is not NUL, then overwrite METAQUOTE with that
2290                         **  character (i.e., METAQUOTE ch is changed to
2291                         **  ch).  p[-1] is used because p is advanced (above).
2292                         */
2293 
2294                         if ((c & 0377) == METAQUOTE && external && *q != '\0')
2295                                 p[-1] = *q++;
2296                 }
2297                 if (sz <= 0)
2298                         break;
2299                 oatomtok = natomtok;
2300                 if (pvp++ == evp)
2301                         break;
2302         }
2303 
2304 #if 0
2305         /*
2306         **  Silently truncate long strings: even though this doesn't
2307         **  seem like a good idea it is necessary because header checks
2308         **  send the whole header value to rscheck() and hence rewrite().
2309         **  The latter however sometimes uses a "short" buffer (e.g.,
2310         **  cbuf[MAXNAME + 1]) to call cataddr() which then triggers this
2311         **  error function.  One possible fix to the problem is to pass
2312         **  flags to rscheck() and rewrite() to distinguish the various
2313         **  calls and only trigger the error if necessary.  For now just
2314         **  undo the change from 8.13.0.
2315         */
2316 
2317         if (sz <= 0)
2318                 usrerr("cataddr: string too long");
2319 #endif
2320         *p = '\0';
2321 
2322         if (tTd(59, 14))
2323                 sm_dprintf("  cataddr => %s\n", str2prt(buf));
2324 }
2325 
2326 /*
2327 **  SAMEADDR -- Determine if two addresses are the same
2328 **
2329 **      This is not just a straight comparison -- if the mailer doesn't
2330 **      care about the host we just ignore it, etc.
2331 **
2332 **      Parameters:
2333 **              a, b -- pointers to the internal forms to compare.
2334 **
2335 **      Returns:
2336 **              true -- they represent the same mailbox.
2337 **              false -- they don't.
2338 **
2339 **      Side Effects:
2340 **              none.
2341 */
2342 
2343 bool
2344 sameaddr(a, b)
2345         register ADDRESS *a;
2346         register ADDRESS *b;
2347 {
2348         register ADDRESS *ca, *cb;
2349 
2350         /* if they don't have the same mailer, forget it */
2351         if (a->q_mailer != b->q_mailer)
2352                 return false;
2353 
2354         /* if the user isn't the same, we can drop out */
2355         if (strcmp(a->q_user, b->q_user) != 0)
2356                 return false;
2357 
2358         /* if we have good uids for both but they differ, these are different */
2359         if (a->q_mailer == ProgMailer)
2360         {
2361                 ca = getctladdr(a);
2362                 cb = getctladdr(b);
2363                 if (ca != NULL && cb != NULL &&
2364                     bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
2365                     ca->q_uid != cb->q_uid)
2366                         return false;
2367         }
2368 
2369         /* otherwise compare hosts (but be careful for NULL ptrs) */
2370         if (a->q_host == b->q_host)
2371         {
2372                 /* probably both null pointers */
2373                 return true;
2374         }
2375         if (a->q_host == NULL || b->q_host == NULL)
2376         {
2377                 /* only one is a null pointer */
2378                 return false;
2379         }
2380         if (strcmp(a->q_host, b->q_host) != 0)
2381                 return false;
2382 
2383         return true;
2384 }
2385 /*
2386 **  PRINTADDR -- print address (for debugging)
2387 **
2388 **      Parameters:
2389 **              a -- the address to print
2390 **              follow -- follow the q_next chain.
2391 **
2392 **      Returns:
2393 **              none.
2394 **
2395 **      Side Effects:
2396 **              none.
2397 */
2398 
2399 struct qflags
2400 {
2401         char            *qf_name;
2402         unsigned long   qf_bit;
2403 };
2404 
2405 static struct qflags    AddressFlags[] =
2406 {
2407         { "QGOODUID",           QGOODUID        },
2408         { "QPRIMARY",           QPRIMARY        },
2409         { "QNOTREMOTE",         QNOTREMOTE      },
2410         { "QSELFREF",           QSELFREF        },
2411         { "QBOGUSSHELL",        QBOGUSSHELL     },
2412         { "QUNSAFEADDR",        QUNSAFEADDR     },
2413         { "QPINGONSUCCESS",     QPINGONSUCCESS  },
2414         { "QPINGONFAILURE",     QPINGONFAILURE  },
2415         { "QPINGONDELAY",       QPINGONDELAY    },
2416         { "QHASNOTIFY",         QHASNOTIFY      },
2417         { "QRELAYED",           QRELAYED        },
2418         { "QEXPANDED",          QEXPANDED       },
2419         { "QDELIVERED",         QDELIVERED      },
2420         { "QDELAYED",           QDELAYED        },
2421         { "QTHISPASS",          QTHISPASS       },
2422         { "QRCPTOK",            QRCPTOK         },
2423         { NULL,                 0               }
2424 };
2425 
2426 void
2427 printaddr(fp, a, follow)
2428         SM_FILE_T *fp;
2429         register ADDRESS *a;
2430         bool follow;
2431 {
2432         register MAILER *m;
2433         MAILER pseudomailer;
2434         register struct qflags *qfp;
2435         bool firstone;
2436 
2437         if (a == NULL)
2438         {
2439                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n");
2440                 return;
2441         }
2442 
2443         while (a != NULL)
2444         {
2445                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", a);
2446                 (void) sm_io_flush(fp, SM_TIME_DEFAULT);
2447 
2448                 /* find the mailer -- carefully */
2449                 m = a->q_mailer;
2450                 if (m == NULL)
2451                 {
2452                         m = &pseudomailer;
2453                         m->m_mno = -1;
2454                         m->m_name = "NULL";
2455                 }
2456 
2457                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2458                                      "%s:\n\tmailer %d (%s), host `%s'\n",
2459                                      a->q_paddr == NULL ? "<null>" : a->q_paddr,
2460                                      m->m_mno, m->m_name,
2461                                      a->q_host == NULL ? "<null>" : a->q_host);
2462                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2463                                      "\tuser `%s', ruser `%s'\n",
2464                                      a->q_user,
2465                                      a->q_ruser == NULL ? "<null>" : a->q_ruser);
2466                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate=");
2467                 switch (a->q_state)
2468                 {
2469                   case QS_OK:
2470                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK");
2471                         break;
2472 
2473                   case QS_DONTSEND:
2474                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2475                                              "DONTSEND");
2476                         break;
2477 
2478                   case QS_BADADDR:
2479                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2480                                              "BADADDR");
2481                         break;
2482 
2483                   case QS_QUEUEUP:
2484                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2485                                              "QUEUEUP");
2486                         break;
2487 
2488                   case QS_RETRY:
2489                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY");
2490                         break;
2491 
2492                   case QS_SENT:
2493                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT");
2494                         break;
2495 
2496                   case QS_VERIFIED:
2497                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2498                                              "VERIFIED");
2499                         break;
2500 
2501                   case QS_EXPANDED:
2502                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2503                                              "EXPANDED");
2504                         break;
2505 
2506                   case QS_SENDER:
2507                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2508                                              "SENDER");
2509                         break;
2510 
2511                   case QS_CLONED:
2512                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2513                                              "CLONED");
2514                         break;
2515 
2516                   case QS_DISCARDED:
2517                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2518                                              "DISCARDED");
2519                         break;
2520 
2521                   case QS_REPLACED:
2522                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2523                                              "REPLACED");
2524                         break;
2525 
2526                   case QS_REMOVED:
2527                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2528                                              "REMOVED");
2529                         break;
2530 
2531                   case QS_DUPLICATE:
2532                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2533                                              "DUPLICATE");
2534                         break;
2535 
2536                   case QS_INCLUDED:
2537                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2538                                              "INCLUDED");
2539                         break;
2540 
2541                   default:
2542                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2543                                              "%d", a->q_state);
2544                         break;
2545                 }
2546                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2547                                      ", next=%p, alias %p, uid %d, gid %d\n",
2548                                      a->q_next, a->q_alias,
2549                                      (int) a->q_uid, (int) a->q_gid);
2550                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<",
2551                                      a->q_flags);
2552                 firstone = true;
2553                 for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++)
2554                 {
2555                         if (!bitset(qfp->qf_bit, a->q_flags))
2556                                 continue;
2557                         if (!firstone)
2558                                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2559                                                      ",");
2560                         firstone = false;
2561                         (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
2562                                              qfp->qf_name);
2563                 }
2564                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n");
2565                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2566                                      "\towner=%s, home=\"%s\", fullname=\"%s\"\n",
2567                                      a->q_owner == NULL ? "(none)" : a->q_owner,
2568                                      a->q_home == NULL ? "(none)" : a->q_home,
2569                                      a->q_fullname == NULL ? "(none)" : a->q_fullname);
2570                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2571                                      "\torcpt=\"%s\", statmta=%s, status=%s\n",
2572                                      a->q_orcpt == NULL ? "(none)" : a->q_orcpt,
2573                                      a->q_statmta == NULL ? "(none)" : a->q_statmta,
2574                                      a->q_status == NULL ? "(none)" : a->q_status);
2575                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2576                                      "\tfinalrcpt=\"%s\"\n",
2577                                      a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt);
2578                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2579                                      "\trstatus=\"%s\"\n",
2580                                      a->q_rstatus == NULL ? "(none)" : a->q_rstatus);
2581                 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2582                                      "\tstatdate=%s\n",
2583                                      a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate));
2584 
2585                 if (!follow)
2586                         return;
2587                 a = a->q_next;
2588         }
2589 }
2590 /*
2591 **  EMPTYADDR -- return true if this address is empty (``<>'')
2592 **
2593 **      Parameters:
2594 **              a -- pointer to the address
2595 **
2596 **      Returns:
2597 **              true -- if this address is "empty" (i.e., no one should
2598 **                      ever generate replies to it.
2599 **              false -- if it is a "regular" (read: replyable) address.
2600 */
2601 
2602 bool
2603 emptyaddr(a)
2604         register ADDRESS *a;
2605 {
2606         return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 ||
2607                a->q_user == NULL || strcmp(a->q_user, "<>") == 0;
2608 }
2609 /*
2610 **  REMOTENAME -- return the name relative to the current mailer
2611 **
2612 **      Parameters:
2613 **              name -- the name to translate.
2614 **              m -- the mailer that we want to do rewriting relative to.
2615 **              flags -- fine tune operations.
2616 **              pstat -- pointer to status word.
2617 **              e -- the current envelope.
2618 **
2619 **      Returns:
2620 **              the text string representing this address relative to
2621 **                      the receiving mailer.
2622 **
2623 **      Side Effects:
2624 **              none.
2625 **
2626 **      Warnings:
2627 **              The text string returned is tucked away locally;
2628 **                      copy it if you intend to save it.
2629 */
2630 
2631 char *
2632 remotename(name, m, flags, pstat, e)
2633         char *name;
2634         struct mailer *m;
2635         int flags;
2636         int *pstat;
2637         register ENVELOPE *e;
2638 {
2639         register char **pvp;
2640         char *SM_NONVOLATILE fancy;
2641         char *oldg;
2642         int rwset;
2643         static char buf[MAXNAME + 1];
2644         char lbuf[MAXNAME + 1];
2645         char pvpbuf[PSBUFSIZE];
2646         char addrtype[4];
2647 
2648         if (tTd(12, 1))
2649         {
2650                 sm_dprintf("remotename(");
2651                 xputs(sm_debug_file(), name);
2652                 sm_dprintf(")\n");
2653         }
2654 
2655         /* don't do anything if we are tagging it as special */
2656         if (bitset(RF_SENDERADDR, flags))
2657         {
2658                 rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset
2659                                                      : m->m_se_rwset;
2660                 addrtype[2] = 's';
2661         }
2662         else
2663         {
2664                 rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset
2665                                                      : m->m_re_rwset;
2666                 addrtype[2] = 'r';
2667         }
2668         if (rwset < 0)
2669                 return name;
2670         addrtype[1] = ' ';
2671         addrtype[3] = '\0';
2672         addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e';
2673         macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype);
2674 
2675         /*
2676         **  Do a heuristic crack of this name to extract any comment info.
2677         **      This will leave the name as a comment and a $g macro.
2678         */
2679 
2680         if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2681                 fancy = "\201g";
2682         else
2683                 fancy = crackaddr(name, e);
2684 
2685         /*
2686         **  Turn the name into canonical form.
2687         **      Normally this will be RFC 822 style, i.e., "user@domain".
2688         **      If this only resolves to "user", and the "C" flag is
2689         **      specified in the sending mailer, then the sender's
2690         **      domain will be appended.
2691         */
2692 
2693         pvp = prescan(name, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, false);
2694         if (pvp == NULL)
2695                 return name;
2696         if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
2697                 *pstat = EX_TEMPFAIL;
2698         if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
2699         {
2700                 /* append from domain to this address */
2701                 register char **pxp = pvp;
2702                 int l = MAXATOM;        /* size of buffer for pvp */
2703 
2704                 /* see if there is an "@domain" in the current name */
2705                 while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2706                 {
2707                         pxp++;
2708                         --l;
2709                 }
2710                 if (*pxp == NULL)
2711                 {
2712                         /* no.... append the "@domain" from the sender */
2713                         register char **qxq = e->e_fromdomain;
2714 
2715                         while ((*pxp++ = *qxq++) != NULL)
2716                         {
2717                                 if (--l <= 0)
2718                                 {
2719                                         *--pxp = NULL;
2720                                         usrerr("553 5.1.0 remotename: too many tokens");
2721                                         *pstat = EX_UNAVAILABLE;
2722                                         break;
2723                                 }
2724                         }
2725                         if (REWRITE(pvp, 3, e) == EX_TEMPFAIL)
2726                                 *pstat = EX_TEMPFAIL;
2727                 }
2728         }
2729 
2730         /*
2731         **  Do more specific rewriting.
2732         **      Rewrite using ruleset 1 or 2 depending on whether this is
2733         **              a sender address or not.
2734         **      Then run it through any receiving-mailer-specific rulesets.
2735         */
2736 
2737         if (bitset(RF_SENDERADDR, flags))
2738         {
2739                 if (REWRITE(pvp, 1, e) == EX_TEMPFAIL)
2740                         *pstat = EX_TEMPFAIL;
2741         }
2742         else
2743         {
2744                 if (REWRITE(pvp, 2, e) == EX_TEMPFAIL)
2745                         *pstat = EX_TEMPFAIL;
2746         }
2747         if (rwset > 0)
2748         {
2749                 if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL)
2750                         *pstat = EX_TEMPFAIL;
2751         }
2752 
2753         /*
2754         **  Do any final sanitation the address may require.
2755         **      This will normally be used to turn internal forms
2756         **      (e.g., user@host.LOCAL) into external form.  This
2757         **      may be used as a default to the above rules.
2758         */
2759 
2760         if (REWRITE(pvp, 4, e) == EX_TEMPFAIL)
2761                 *pstat = EX_TEMPFAIL;
2762 
2763         /*
2764         **  Now restore the comment information we had at the beginning.
2765         */
2766 
2767         cataddr(pvp, NULL, lbuf, sizeof(lbuf), '\0', false);
2768         oldg = macget(&e->e_macro, 'g');
2769         macset(&e->e_macro, 'g', lbuf);
2770 
2771         SM_TRY
2772                 /* need to make sure route-addrs have <angle brackets> */
2773                 if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
2774                         expand("<\201g>", buf, sizeof(buf), e);
2775                 else
2776                         expand(fancy, buf, sizeof(buf), e);
2777         SM_FINALLY
2778                 macset(&e->e_macro, 'g', oldg);
2779         SM_END_TRY
2780 
2781         if (tTd(12, 1))
2782         {
2783                 sm_dprintf("remotename => `");
2784                 xputs(sm_debug_file(), buf);
2785                 sm_dprintf("'\n");
2786         }
2787         return buf;
2788 }
2789 /*
2790 **  MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2791 **
2792 **      Parameters:
2793 **              a -- the address to map (but just the user name part).
2794 **              sendq -- the sendq in which to install any replacement
2795 **                      addresses.
2796 **              aliaslevel -- the alias nesting depth.
2797 **              e -- the envelope.
2798 **
2799 **      Returns:
2800 **              none.
2801 */
2802 
2803 #define Q_COPYFLAGS     (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\
2804                          Q_PINGFLAGS|QHASNOTIFY|\
2805                          QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\
2806                          QBYTRACE|QBYNDELAY|QBYNRELAY)
2807 
2808 void
2809 maplocaluser(a, sendq, aliaslevel, e)
2810         register ADDRESS *a;
2811         ADDRESS **sendq;
2812         int aliaslevel;
2813         ENVELOPE *e;
2814 {
2815         register char **pvp;
2816         register ADDRESS *SM_NONVOLATILE a1 = NULL;
2817         char pvpbuf[PSBUFSIZE];
2818 
2819         if (tTd(29, 1))
2820         {
2821                 sm_dprintf("maplocaluser: ");
2822                 printaddr(sm_debug_file(), a, false);
2823         }
2824         pvp = prescan(a->q_user, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL,
2825                         false);
2826         if (pvp == NULL)
2827         {
2828                 if (tTd(29, 9))
2829                         sm_dprintf("maplocaluser: cannot prescan %s\n",
2830                                 a->q_user);
2831                 return;
2832         }
2833 
2834         macdefine(&e->e_macro, A_PERM, 'h', a->q_host);
2835         macdefine(&e->e_macro, A_PERM, 'u', a->q_user);
2836         macdefine(&e->e_macro, A_PERM, 'z', a->q_home);
2837 
2838         macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r");
2839         if (REWRITE(pvp, 5, e) == EX_TEMPFAIL)
2840         {
2841                 if (tTd(29, 9))
2842                         sm_dprintf("maplocaluser: rewrite tempfail\n");
2843                 a->q_state = QS_QUEUEUP;
2844                 a->q_status = "4.4.3";
2845                 return;
2846         }
2847         if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
2848         {
2849                 if (tTd(29, 9))
2850                         sm_dprintf("maplocaluser: doesn't resolve\n");
2851                 return;
2852         }
2853 
2854         SM_TRY
2855                 a1 = buildaddr(pvp, NULL, 0, e);
2856         SM_EXCEPT(exc, "E:mta.quickabort")
2857 
2858                 /*
2859                 **  mark address as bad, S5 returned an error
2860                 **      and we gave that back to the SMTP client.
2861                 */
2862 
2863                 a->q_state = QS_DONTSEND;
2864                 sm_exc_raisenew_x(&EtypeQuickAbort, 2);
2865         SM_END_TRY
2866 
2867         /* if non-null, mailer destination specified -- has it changed? */
2868         if (a1 == NULL || sameaddr(a, a1))
2869         {
2870                 if (tTd(29, 9))
2871                         sm_dprintf("maplocaluser: address unchanged\n");
2872                 return;
2873         }
2874 
2875         /* make new address take on flags and print attributes of old */
2876         a1->q_flags &= ~Q_COPYFLAGS;
2877         a1->q_flags |= a->q_flags & Q_COPYFLAGS;
2878         a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr);
2879         a1->q_finalrcpt = a->q_finalrcpt;
2880         a1->q_orcpt = a->q_orcpt;
2881 
2882         /* mark old address as dead; insert new address */
2883         a->q_state = QS_REPLACED;
2884         if (tTd(29, 5))
2885         {
2886                 sm_dprintf("maplocaluser: QS_REPLACED ");
2887                 printaddr(sm_debug_file(), a, false);
2888         }
2889         a1->q_alias = a;
2890         allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e);
2891         (void) recipient(a1, sendq, aliaslevel, e);
2892 }
2893 /*
2894 **  DEQUOTE_INIT -- initialize dequote map
2895 **
2896 **      Parameters:
2897 **              map -- the internal map structure.
2898 **              args -- arguments.
2899 **
2900 **      Returns:
2901 **              true.
2902 */
2903 
2904 bool
2905 dequote_init(map, args)
2906         MAP *map;
2907         char *args;
2908 {
2909         register char *p = args;
2910 
2911         /* there is no check whether there is really an argument */
2912         map->map_mflags |= MF_KEEPQUOTES;
2913         for (;;)
2914         {
2915                 while (isascii(*p) && isspace(*p))
2916                         p++;
2917                 if (*p != '-')
2918                         break;
2919                 switch (*++p)
2920                 {
2921                   case 'a':
2922                         map->map_app = ++p;
2923                         break;
2924 
2925                   case 'D':
2926                         map->map_mflags |= MF_DEFER;
2927                         break;
2928 
2929                   case 'S':
2930                   case 's':
2931                         map->map_spacesub = *++p;
2932                         break;
2933                 }
2934                 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2935                         p++;
2936                 if (*p != '\0')
2937                         *p = '\0';
2938         }
2939         if (map->map_app != NULL)
2940                 map->map_app = newstr(map->map_app);
2941 
2942         return true;
2943 }
2944 /*
2945 **  DEQUOTE_MAP -- unquote an address
2946 **
2947 **      Parameters:
2948 **              map -- the internal map structure (ignored).
2949 **              name -- the name to dequote.
2950 **              av -- arguments (ignored).
2951 **              statp -- pointer to status out-parameter.
2952 **
2953 **      Returns:
2954 **              NULL -- if there were no quotes, or if the resulting
2955 **                      unquoted buffer would not be acceptable to prescan.
2956 **              else -- The dequoted buffer.
2957 */
2958 
2959 /* ARGSUSED2 */
2960 char *
2961 dequote_map(map, name, av, statp)
2962         MAP *map;
2963         char *name;
2964         char **av;
2965         int *statp;
2966 {
2967         register char *p;
2968         register char *q;
2969         register char c;
2970         int anglecnt = 0;
2971         int cmntcnt = 0;
2972         int quotecnt = 0;
2973         int spacecnt = 0;
2974         bool quotemode = false;
2975         bool bslashmode = false;
2976         char spacesub = map->map_spacesub;
2977 
2978         for (p = q = name; (c = *p++) != '\0'; )
2979         {
2980                 if (bslashmode)
2981                 {
2982                         bslashmode = false;
2983                         *q++ = c;
2984                         continue;
2985                 }
2986 
2987                 if (c == ' ' && spacesub != '\0')
2988                         c = spacesub;
2989 
2990                 switch (c)
2991                 {
2992                   case '\\':
2993                         bslashmode = true;
2994                         break;
2995 
2996                   case '(':
2997                         cmntcnt++;
2998                         break;
2999 
3000                   case ')':
3001                         if (cmntcnt-- <= 0)
3002                                 return NULL;
3003                         break;
3004 
3005                   case ' ':
3006                   case '\t':
3007                         spacecnt++;
3008                         break;
3009                 }
3010 
3011                 if (cmntcnt > 0)
3012                 {
3013                         *q++ = c;
3014                         continue;
3015                 }
3016 
3017                 switch (c)
3018                 {
3019                   case '"':
3020                         quotemode = !quotemode;
3021                         quotecnt++;
3022                         continue;
3023 
3024                   case '<':
3025                         anglecnt++;
3026                         break;
3027 
3028                   case '>':
3029                         if (anglecnt-- <= 0)
3030                                 return NULL;
3031                         break;
3032                 }
3033                 *q++ = c;
3034         }
3035 
3036         if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
3037             quotemode || quotecnt <= 0 || spacecnt != 0)
3038                 return NULL;
3039         *q++ = '\0';
3040         return map_rewrite(map, name, strlen(name), NULL);
3041 }
3042 /*
3043 **  RSCHECK -- check string(s) for validity using rewriting sets
3044 **
3045 **      Parameters:
3046 **              rwset -- the rewriting set to use.
3047 **              p1 -- the first string to check.
3048 **              p2 -- the second string to check -- may be null.
3049 **              e -- the current envelope.
3050 **              flags -- control some behavior, see RSF_ in sendmail.h
3051 **              logl -- logging level.
3052 **              host -- NULL or relay host.
3053 **              logid -- id for sm_syslog.
3054 **              addr -- if not NULL and ruleset returns $#error:
3055 **                              store mailer triple here.
3056 **
3057 **      Returns:
3058 **              EX_OK -- if the rwset doesn't resolve to $#error
3059 **              else -- the failure status (message printed)
3060 */
3061 
3062 int
3063 rscheck(rwset, p1, p2, e, flags, logl, host, logid, addr)
3064         char *rwset;
3065         char *p1;
3066         char *p2;
3067         ENVELOPE *e;
3068         int flags;
3069         int logl;
3070         char *host;
3071         char *logid;
3072         ADDRESS *addr;
3073 {
3074         char *volatile buf;
3075         size_t bufsize;
3076         int saveexitstat;
3077         int volatile rstat = EX_OK;
3078         char **pvp;
3079         int rsno;
3080         bool volatile discard = false;
3081         bool saveQuickAbort = QuickAbort;
3082         bool saveSuprErrs = SuprErrs;
3083         bool quarantine = false;
3084         char ubuf[BUFSIZ * 2];
3085         char buf0[MAXLINE];
3086         char pvpbuf[PSBUFSIZE];
3087         extern char MsgBuf[];
3088 
3089         if (tTd(48, 2))
3090                 sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1,
3091                         p2 == NULL ? "(NULL)" : p2);
3092 
3093         rsno = strtorwset(rwset, NULL, ST_FIND);
3094         if (rsno < 0)
3095                 return EX_OK;
3096 
3097         if (p2 != NULL)
3098         {
3099                 bufsize = strlen(p1) + strlen(p2) + 2;
3100                 if (bufsize > sizeof(buf0))
3101                         buf = sm_malloc_x(bufsize);
3102                 else
3103                 {
3104                         buf = buf0;
3105                         bufsize = sizeof(buf0);
3106                 }
3107                 (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
3108         }
3109         else
3110         {
3111                 bufsize = strlen(p1) + 1;
3112                 if (bufsize > sizeof(buf0))
3113                         buf = sm_malloc_x(bufsize);
3114                 else
3115                 {
3116                         buf = buf0;
3117                         bufsize = sizeof(buf0);
3118                 }
3119                 (void) sm_strlcpy(buf, p1, bufsize);
3120         }
3121         SM_TRY
3122         {
3123                 SuprErrs = true;
3124                 QuickAbort = false;
3125                 pvp = prescan(buf, '\0', pvpbuf, sizeof(pvpbuf), NULL,
3126                               bitset(RSF_RMCOMM, flags) ?
3127                                         IntTokenTab : TokTypeNoC,
3128                               bitset(RSF_RMCOMM, flags) ? false : true);
3129                 SuprErrs = saveSuprErrs;
3130                 if (pvp == NULL)
3131                 {
3132                         if (tTd(48, 2))
3133                                 sm_dprintf("rscheck: cannot prescan input\n");
3134         /*
3135                         syserr("rscheck: cannot prescan input: \"%s\"",
3136                                 shortenstring(buf, MAXSHORTSTR));
3137                         rstat = EX_DATAERR;
3138         */
3139                         goto finis;
3140                 }
3141                 if (bitset(RSF_UNSTRUCTURED, flags))
3142                         SuprErrs = true;
3143                 (void) REWRITE(pvp, rsno, e);
3144                 if (bitset(RSF_UNSTRUCTURED, flags))
3145                         SuprErrs = saveSuprErrs;
3146                 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET ||
3147                     pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 &&
3148                                        strcmp(pvp[1], "discard") != 0))
3149                 {
3150                         goto finis;
3151                 }
3152 
3153                 if (strcmp(pvp[1], "discard") == 0)
3154                 {
3155                         if (tTd(48, 2))
3156                                 sm_dprintf("rscheck: discard mailer selected\n");
3157                         e->e_flags |= EF_DISCARD;
3158                         discard = true;
3159                 }
3160                 else if (strcmp(pvp[1], "error") == 0 &&
3161                          pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST &&
3162                          pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0)
3163                 {
3164                         if (pvp[4] == NULL ||
3165                             (pvp[4][0] & 0377) != CANONUSER ||
3166                             pvp[5] == NULL)
3167                                 e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3168                                                                  rwset);
3169                         else
3170                         {
3171                                 cataddr(&(pvp[5]), NULL, ubuf,
3172                                         sizeof(ubuf), ' ', true);
3173                                 e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
3174                                                                  ubuf);
3175                         }
3176                         macdefine(&e->e_macro, A_PERM,
3177                                   macid("{quarantine}"), e->e_quarmsg);
3178                         quarantine = true;
3179                 }
3180                 else
3181                 {
3182                         auto ADDRESS a1;
3183                         int savelogusrerrs = LogUsrErrs;
3184                         static bool logged = false;
3185 
3186                         /* got an error -- process it */
3187                         saveexitstat = ExitStat;
3188                         LogUsrErrs = false;
3189                         (void) buildaddr(pvp, &a1, 0, e);
3190                         if (addr != NULL)
3191                         {
3192                                 addr->q_mailer = a1.q_mailer;
3193                                 addr->q_user = a1.q_user;
3194                                 addr->q_host = a1.q_host;
3195                         }
3196                         LogUsrErrs = savelogusrerrs;
3197                         rstat = ExitStat;
3198                         ExitStat = saveexitstat;
3199                         if (!logged)
3200                         {
3201                                 if (bitset(RSF_COUNT, flags))
3202                                         markstats(e, &a1, STATS_REJECT);
3203                                 logged = true;
3204                         }
3205                 }
3206 
3207                 if (LogLevel > logl)
3208                 {
3209                         char *relay;
3210                         char *p;
3211                         char lbuf[MAXLINE];
3212 
3213                         p = lbuf;
3214                         if (p2 != NULL)
3215                         {
3216                                 (void) sm_snprintf(p, SPACELEFT(lbuf, p),
3217                                         ", arg2=%s",
3218                                         p2);
3219                                 p += strlen(p);
3220                         }
3221 
3222                         if (host != NULL)
3223                                 relay = host;
3224                         else
3225                                 relay = macvalue('_', e);
3226                         if (relay != NULL)
3227                         {
3228                                 (void) sm_snprintf(p, SPACELEFT(lbuf, p),
3229                                         ", relay=%s", relay);
3230                                 p += strlen(p);
3231                         }
3232                         *p = '\0';
3233                         if (discard)
3234                                 sm_syslog(LOG_NOTICE, logid,
3235                                           "ruleset=%s, arg1=%s%s, discard",
3236                                           rwset, p1, lbuf);
3237                         else if (quarantine)
3238                                 sm_syslog(LOG_NOTICE, logid,
3239                                           "ruleset=%s, arg1=%s%s, quarantine=%s",
3240                                           rwset, p1, lbuf, ubuf);
3241                         else
3242                                 sm_syslog(LOG_NOTICE, logid,
3243                                           "ruleset=%s, arg1=%s%s, reject=%s",
3244                                           rwset, p1, lbuf, MsgBuf);
3245                 }
3246 
3247          finis: ;
3248         }
3249         SM_FINALLY
3250         {
3251                 /* clean up */
3252                 if (buf != buf0)
3253                         sm_free(buf);
3254                 QuickAbort = saveQuickAbort;
3255         }
3256         SM_END_TRY
3257 
3258         setstat(rstat);
3259 
3260         /* rulesets don't set errno */
3261         errno = 0;
3262         if (rstat != EX_OK && QuickAbort)
3263                 sm_exc_raisenew_x(&EtypeQuickAbort, 2);
3264         return rstat;
3265 }
3266 /*
3267 **  RSCAP -- call rewriting set to return capabilities
3268 **
3269 **      Parameters:
3270 **              rwset -- the rewriting set to use.
3271 **              p1 -- the first string to check.
3272 **              p2 -- the second string to check -- may be null.
3273 **              e -- the current envelope.
3274 **              pvp -- pointer to token vector.
3275 **              pvpbuf -- buffer space.
3276 **              size -- size of buffer space.
3277 **
3278 **      Returns:
3279 **              EX_UNAVAILABLE -- ruleset doesn't exist.
3280 **              EX_DATAERR -- prescan() failed.
3281 **              EX_OK -- rewrite() was successful.
3282 **              else -- return status from rewrite().
3283 */
3284 
3285 int
3286 rscap(rwset, p1, p2, e, pvp, pvpbuf, size)
3287         char *rwset;
3288         char *p1;
3289         char *p2;
3290         ENVELOPE *e;
3291         char ***pvp;
3292         char *pvpbuf;
3293         int size;
3294 {
3295         char *volatile buf;
3296         size_t bufsize;
3297         int volatile rstat = EX_OK;
3298         int rsno;
3299         bool saveQuickAbort = QuickAbort;
3300         bool saveSuprErrs = SuprErrs;
3301         char buf0[MAXLINE];
3302         extern char MsgBuf[];
3303 
3304         if (tTd(48, 2))
3305                 sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1,
3306                         p2 == NULL ? "(NULL)" : p2);
3307 
3308         SM_REQUIRE(pvp != NULL);
3309         rsno = strtorwset(rwset, NULL, ST_FIND);
3310         if (rsno < 0)
3311                 return EX_UNAVAILABLE;
3312 
3313         if (p2 != NULL)
3314         {
3315                 bufsize = strlen(p1) + strlen(p2) + 2;
3316                 if (bufsize > sizeof(buf0))
3317                         buf = sm_malloc_x(bufsize);
3318                 else
3319                 {
3320                         buf = buf0;
3321                         bufsize = sizeof(buf0);
3322                 }
3323                 (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2);
3324         }
3325         else
3326         {
3327                 bufsize = strlen(p1) + 1;
3328                 if (bufsize > sizeof(buf0))
3329                         buf = sm_malloc_x(bufsize);
3330                 else
3331                 {
3332                         buf = buf0;
3333                         bufsize = sizeof(buf0);
3334                 }
3335                 (void) sm_strlcpy(buf, p1, bufsize);
3336         }
3337         SM_TRY
3338         {
3339                 SuprErrs = true;
3340                 QuickAbort = false;
3341                 *pvp = prescan(buf, '\0', pvpbuf, size, NULL, IntTokenTab,
3342                                 false);
3343                 if (*pvp != NULL)
3344                         rstat = rewrite(*pvp, rsno, 0, e, size);
3345                 else
3346                 {
3347                         if (tTd(48, 2))
3348                                 sm_dprintf("rscap: cannot prescan input\n");
3349                         rstat = EX_DATAERR;
3350                 }
3351         }
3352         SM_FINALLY
3353         {
3354                 /* clean up */
3355                 if (buf != buf0)
3356                         sm_free(buf);
3357                 SuprErrs = saveSuprErrs;
3358                 QuickAbort = saveQuickAbort;
3359 
3360                 /* prevent information leak, this may contain rewrite error */
3361                 MsgBuf[0] = '\0';
3362         }
3363         SM_END_TRY
3364         return rstat;
3365 }