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