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 = 0; 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 { 159 int len = 0; 160 161 a->q_user = quote_internal_chars(a->q_user, NULL, &len, e->e_rpool); /* EAI: ok */ 162 } 163 #endif 164 165 if (hasctrlchar(a->q_user, isrcpt, true)) 166 { 167 if (tTd(20, 1)) 168 sm_dprintf("parseaddr-->bad q_user\n"); 169 170 /* 171 ** Just mark the address as bad so DSNs work. 172 ** hasctrlchar() has to make sure that the address 173 ** has been sanitized, e.g., shortened. 174 */ 175 176 a->q_state = QS_BADADDR; 177 } 178 179 /* 180 ** Make local copies of the host & user and then 181 ** transport them out. 182 */ 183 184 allocaddr(a, flags, addr, e); 185 e->e_flags &= ~EF_SECURE; 186 if (QS_IS_BADADDR(a->q_state)) 187 { 188 /* weed out bad characters in the printable address too */ 189 (void) hasctrlchar(a->q_paddr, isrcpt, false); 190 return a; 191 } 192 193 /* 194 ** Select a queue directory for recipient addresses. 195 ** This is done here and in split_across_queue_groups(), 196 ** but the latter applies to addresses after aliasing, 197 ** and only if splitting is done. 198 */ 199 200 if ((a->q_qgrp == NOAQGRP || a->q_qgrp == ENVQGRP) && 201 !bitset(RF_SENDERADDR|RF_HEADERADDR|RF_RM_ADDR, flags) && 202 OpMode != MD_INITALIAS) 203 { 204 int r; 205 206 /* call ruleset which should return a queue group name */ 207 r = rscap(RS_QUEUEGROUP, a->q_user, NULL, e, &pvp, pvpbuf, 208 sizeof(pvpbuf)); 209 if (r == EX_OK && 210 pvp != NULL && pvp[0] != NULL && 211 (pvp[0][0] & 0377) == CANONNET && 212 pvp[1] != NULL && pvp[1][0] != '\0') 213 { 214 r = name2qid(pvp[1]); 215 if (r == NOQGRP && LogLevel > 10) 216 sm_syslog(LOG_INFO, NOQID, 217 "can't find queue group name %s, selection ignored", 218 pvp[1]); 219 if (tTd(20, 4) && r != NOQGRP) 220 sm_syslog(LOG_INFO, NOQID, 221 "queue group name %s -> %d", 222 pvp[1], r); 223 a->q_qgrp = r == NOQGRP ? ENVQGRP : r; 224 } 225 } 226 227 /* 228 ** If there was a parsing failure, mark it for queueing. 229 */ 230 231 if (qup && OpMode != MD_INITALIAS) 232 { 233 char *msg = "Transient parse error -- message queued for future delivery"; 234 235 if (e->e_sendmode == SM_DEFER) 236 msg = "Deferring message until queue run"; 237 if (tTd(20, 1)) 238 sm_dprintf("parseaddr: queueing message\n"); 239 message("%s", msg); 240 if (e->e_message == NULL && e->e_sendmode != SM_DEFER) 241 e->e_message = sm_rpool_strdup_x(e->e_rpool, msg); 242 a->q_state = QS_QUEUEUP; 243 a->q_status = "4.4.3"; 244 } 245 246 /* 247 ** Compute return value. 248 */ 249 250 if (tTd(20, 1)) 251 { 252 sm_dprintf("parseaddr-->"); 253 printaddr(sm_debug_file(), a, false); 254 } 255 256 return a; 257 } 258 259 /* 260 ** INVALIDADDR -- check for address containing characters used for macros 261 ** 262 ** Parameters: 263 ** addr -- the address to check. 264 ** note: this is the complete address (including display part) 265 ** delimptr -- if non-NULL: end of address to check, i.e., 266 ** a pointer in the address string. 267 ** isrcpt -- true iff the address is for a recipient. 268 ** 269 ** Returns: 270 ** true -- if the address has characters that are reservered 271 ** for macros or is too long. 272 ** false -- otherwise. 273 */ 274 275 bool 276 invalidaddr(addr, delimptr, isrcpt) 277 register char *addr; 278 char *delimptr; 279 bool isrcpt; 280 { 281 bool result = false; 282 char savedelim = '\0'; 283 char *b = addr; 284 XLENDECL 285 286 if (delimptr != NULL) 287 { 288 /* delimptr points to the end of the address to test */ 289 savedelim = *delimptr; 290 if (savedelim != '\0') /* if that isn't '\0' already: */ 291 *delimptr = '\0'; /* set it */ 292 } 293 for (; *addr != '\0'; addr++) 294 { 295 #if !USE_EAI 296 if (!EightBitAddrOK && (*addr & 0340) == 0200) 297 { 298 setstat(EX_USAGE); 299 result = true; 300 *addr = BAD_CHAR_REPLACEMENT; 301 } 302 #endif 303 XLEN(*addr); 304 if (xlen > MAXNAME - 1) /* EAI:ok */ 305 { 306 char saved = *addr; 307 308 *addr = '\0'; 309 usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)", 310 b, MAXNAME - 1); /* EAI:ok */ 311 *addr = saved; 312 result = true; 313 goto delim; 314 } 315 } 316 #if USE_EAI 317 /* check for valid UTF8 string? */ 318 #endif 319 if (result) 320 { 321 if (isrcpt) 322 usrerr("501 5.1.3 8-bit character in mailbox address \"%s\"", 323 b); 324 else 325 usrerr("501 5.1.7 8-bit character in mailbox address \"%s\"", 326 b); 327 } 328 delim: 329 if (delimptr != NULL && savedelim != '\0') 330 *delimptr = savedelim; /* restore old character at delimptr */ 331 return result; 332 } 333 334 /* 335 ** HASCTRLCHAR -- check for address containing meta-characters 336 ** 337 ** Checks that the address contains no meta-characters, and contains 338 ** no "non-printable" characters unless they are quoted or escaped. 339 ** Quoted or escaped characters are literals. 340 ** 341 ** Parameters: 342 ** addr -- the address to check. 343 ** isrcpt -- true if the address is for a recipient; false 344 ** indicates a from. 345 ** complain -- true if an error should issued if the address 346 ** is invalid and should be "repaired". 347 ** 348 ** Returns: 349 ** true -- if the address has any "weird" characters or 350 ** non-printable characters or if a quote is unbalanced. 351 ** false -- otherwise. 352 ** 353 ** Side Effects: 354 ** Might invoke shorten_rfc822_string() to change addr in place. 355 */ 356 357 static bool 358 hasctrlchar(addr, isrcpt, complain) 359 register char *addr; 360 bool isrcpt, complain; 361 { 362 bool quoted = false; 363 char *result = NULL; 364 char *b = addr; 365 XLENDECL 366 367 if (addr == NULL) 368 return false; 369 for (; *addr != '\0'; addr++) 370 { 371 XLEN(*addr); 372 if (xlen > MAXNAME - 1) /* EAI:ok */ 373 { 374 if (complain) 375 { 376 (void) shorten_rfc822_string(b, MAXNAME - 1); /* EAI:ok */ 377 usrerr("553 5.1.0 Address \"%s\" too long (%d bytes max)", 378 b, MAXNAME - 1); /* EAI:ok */ 379 return true; 380 } 381 result = "too long"; 382 } 383 if (!quoted && ((unsigned char)*addr < 32 || *addr == 127)) 384 { 385 result = "non-printable character"; 386 *addr = BAD_CHAR_REPLACEMENT; 387 continue; 388 } 389 if (*addr == '"') 390 quoted = !quoted; 391 else if (*addr == '\\') 392 { 393 /* XXX Generic problem: no '\0' in strings. */ 394 if (*++addr == '\0') 395 { 396 result = "trailing \\ character"; 397 *--addr = BAD_CHAR_REPLACEMENT; 398 break; 399 } 400 } 401 if (!SMTPUTF8 && !EightBitAddrOK && (*addr & 0340) == 0200) 402 { 403 setstat(EX_USAGE); 404 result = "8-bit character"; 405 *addr = BAD_CHAR_REPLACEMENT; 406 continue; 407 } 408 } 409 if (quoted) 410 result = "unbalanced quote"; /* unbalanced quote */ 411 if (result != NULL && complain) 412 { 413 if (isrcpt) 414 usrerr("501 5.1.3 Syntax error in mailbox address \"%s\" (%s)", 415 b, result); 416 else 417 usrerr("501 5.1.7 Syntax error in mailbox address \"%s\" (%s)", 418 b, result); 419 } 420 return result != NULL; 421 } 422 423 /* 424 ** ALLOCADDR -- do local allocations of address on demand. 425 ** 426 ** Also lowercases the host name if requested. 427 ** 428 ** Parameters: 429 ** a -- the address to reallocate. 430 ** flags -- the copy flag (see RF_ definitions in sendmail.h 431 ** for a description). 432 ** paddr -- the printname of the address. 433 ** e -- envelope 434 ** 435 ** Returns: 436 ** none. 437 ** 438 ** Side Effects: 439 ** Copies portions of a into local buffers as requested. 440 */ 441 442 static void 443 allocaddr(a, flags, paddr, e) 444 register ADDRESS *a; 445 int flags; 446 char *paddr; 447 ENVELOPE *e; 448 { 449 if (tTd(24, 4)) 450 sm_dprintf("allocaddr(flags=%x, paddr=%s, ad=%d)\n", flags, paddr, bitset(EF_SECURE, e->e_flags)); 451 452 a->q_paddr = paddr; 453 454 if (a->q_user == NULL) 455 a->q_user = ""; 456 if (a->q_host == NULL) 457 a->q_host = ""; 458 459 if (bitset(EF_SECURE, e->e_flags)) 460 a->q_flags |= QSECURE; 461 462 if (bitset(RF_COPYPARSE, flags)) 463 { 464 a->q_host = sm_rpool_strdup_x(e->e_rpool, a->q_host); 465 if (a->q_user != a->q_paddr) 466 a->q_user = sm_rpool_strdup_x(e->e_rpool, a->q_user); 467 } 468 469 if (a->q_paddr == NULL) 470 a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 471 a->q_qgrp = NOAQGRP; 472 } 473 474 /* 475 ** PRESCAN -- Prescan name and make it canonical 476 ** 477 ** Scans a name and turns it into a set of tokens. This process 478 ** deletes blanks and comments (in parentheses) (if the token type 479 ** for left paren is SPC). 480 ** 481 ** This routine knows about quoted strings and angle brackets. 482 ** 483 ** There are certain subtleties to this routine. The one that 484 ** comes to mind now is that backslashes on the ends of names 485 ** are silently stripped off; this is intentional. The problem 486 ** is that some versions of sndmsg (like at LBL) set the kill 487 ** character to something other than @ when reading addresses; 488 ** so people type "csvax.eric\@berkeley" -- which screws up the 489 ** berknet mailer. 490 ** 491 ** Parameters: 492 ** addr -- the name to chomp. 493 ** delim -- the delimiter for the address, normally 494 ** '\0' or ','; \0 is accepted in any case. 495 ** If '\t' then we are reading the .cf file. 496 ** pvpbuf -- place to put the saved text -- note that 497 ** the pointers are static. 498 ** pvpbsize -- size of pvpbuf. 499 ** delimptr -- if non-NULL, set to the location of the 500 ** terminating delimiter. 501 ** toktab -- if set, a token table to use for parsing. 502 ** If NULL, use the default table. 503 ** ignore -- if true, ignore unbalanced addresses 504 ** 505 ** Returns: 506 ** A pointer to a vector of tokens. 507 ** NULL on error. 508 */ 509 510 /* states and character types */ 511 #define OPR 0 /* operator */ 512 #define ATM 1 /* atom */ 513 #define QST 2 /* in quoted string */ 514 #define SPC 3 /* chewing up spaces */ 515 #define ONE 4 /* pick up one character */ 516 #define ILL 5 /* illegal character */ 517 518 #define NSTATES 6 /* number of states */ 519 #define TYPE 017 /* mask to select state type */ 520 521 /* meta bits for table */ 522 #define M 020 /* meta character; don't pass through */ 523 #define B 040 /* cause a break */ 524 #define MB M|B /* meta-break */ 525 526 static short StateTab[NSTATES][NSTATES] = 527 { 528 /* oldst chtype> OPR ATM QST SPC ONE ILL */ 529 /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, 530 /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, 531 /*QST*/ { QST, QST, OPR, QST, QST, QST }, 532 /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, 533 /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, 534 /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M } 535 }; 536 537 /* these all get modified with the OperatorChars */ 538 539 /* token type table for external strings */ 540 unsigned char ExtTokenTab[256] = 541 { 542 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 543 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 544 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 545 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 546 /* sp ! " # $ % & ' ( ) * + , - . / */ 547 SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 548 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 549 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 550 /* @ A B C D E F G H I J K L M N O */ 551 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 552 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 553 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 554 /* ` a b c d e f g h i j k l m n o */ 555 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 556 /* p q r s t u v w x y z { | } ~ del */ 557 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 558 559 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 560 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 561 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 562 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 563 /* sp ! " # $ % & ' ( ) * + , - . / */ 564 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 565 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 566 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 567 /* @ A B C D E F G H I J K L M N O */ 568 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 569 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 570 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 571 /* ` a b c d e f g h i j k l m n o */ 572 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 573 /* p q r s t u v w x y z { | } ~ del */ 574 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM 575 }; 576 577 /* token type table for internal strings */ 578 unsigned char IntTokenTab[256] = 579 { 580 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 581 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 582 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 583 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 584 /* sp ! " # $ % & ' ( ) * + , - . / */ 585 SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,ATM,ATM,ATM,ATM, 586 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 587 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 588 /* @ A B C D E F G H I J K L M N O */ 589 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 590 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 591 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 592 /* ` a b c d e f g h i j k l m n o */ 593 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 594 /* p q r s t u v w x y z { | } ~ del */ 595 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 596 597 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 598 OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 599 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 600 OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 601 /* sp ! " # $ % & ' ( ) * + , - . / */ 602 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 603 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 604 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 605 /* @ A B C D E F G H I J K L M N O */ 606 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 607 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 608 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 609 /* ` a b c d e f g h i j k l m n o */ 610 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 611 /* p q r s t u v w x y z { | } ~ del */ 612 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE 613 }; 614 615 /* token type table for MIME parsing */ 616 unsigned char MimeTokenTab[256] = 617 { 618 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 619 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL, 620 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 621 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 622 /* sp ! " # $ % & ' ( ) * + , - . / */ 623 SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, SPC,SPC,ATM,ATM,OPR,ATM,ATM,OPR, 624 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 625 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR, 626 /* @ A B C D E F G H I J K L M N O */ 627 OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 628 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 629 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM, 630 /* ` a b c d e f g h i j k l m n o */ 631 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 632 /* p q r s t u v w x y z { | } ~ del */ 633 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 634 635 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 636 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 637 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 638 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 639 /* sp ! " # $ % & ' ( ) * + , - . / */ 640 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 641 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 642 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 643 /* @ A B C D E F G H I J K L M N O */ 644 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 645 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 646 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 647 /* ` a b c d e f g h i j k l m n o */ 648 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, 649 /* p q r s t u v w x y z { | } ~ del */ 650 ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ONE 651 }; 652 653 /* token type table: don't strip comments */ 654 unsigned char TokTypeNoC[256] = 655 { 656 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 657 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, 658 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 659 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 660 /* sp ! " # $ % & ' ( ) * + , - . / */ 661 SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, OPR,OPR,ATM,ATM,ATM,ATM,ATM,ATM, 662 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 663 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 664 /* @ A B C D E F G H I J K L M N O */ 665 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 666 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 667 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 668 /* ` a b c d e f g h i j k l m n o */ 669 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 670 /* p q r s t u v w x y z { | } ~ del */ 671 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 672 673 /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ 674 OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 675 /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ 676 OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, 677 /* sp ! " # $ % & ' ( ) * + , - . / */ 678 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 679 /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ 680 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 681 /* @ A B C D E F G H I J K L M N O */ 682 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 683 /* P Q R S T U V W X Y Z [ \ ] ^ _ */ 684 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 685 /* ` a b c d e f g h i j k l m n o */ 686 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, 687 /* p q r s t u v w x y z { | } ~ del */ 688 ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ONE 689 }; 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 2019 if (tTd(24, 5)) 2020 { 2021 sm_dprintf("buildaddr, flags=%x, tv=", flags); 2022 printav(sm_debug_file(), tv); 2023 } 2024 2025 maxatom = MAXATOM; 2026 if (a == NULL) 2027 a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a)); 2028 memset((char *) a, '\0', sizeof(*a)); 2029 hbuf[0] = '\0'; 2030 2031 /* set up default error return flags */ 2032 a->q_flags |= DefaultNotify; 2033 2034 /* figure out what net/mailer to use */ 2035 if (*tv == NULL || (**tv & 0377) != CANONNET) 2036 { 2037 syserr("554 5.3.5 buildaddr: no mailer in parsed address"); 2038 badaddr: 2039 /* 2040 ** ExitStat may have been set by an earlier map open 2041 ** failure (to a permanent error (EX_OSERR) in syserr()) 2042 ** so we also need to check if this particular $#error 2043 ** return wanted a 4XX failure. 2044 ** 2045 ** XXX the real fix is probably to set ExitStat correctly, 2046 ** i.e., to EX_TEMPFAIL if the map open is just a temporary 2047 ** error. 2048 */ 2049 2050 if (ExitStat == EX_TEMPFAIL || tempfail) 2051 a->q_state = QS_QUEUEUP; 2052 else 2053 { 2054 a->q_state = QS_BADADDR; 2055 a->q_mailer = &errormailer; 2056 } 2057 return a; 2058 } 2059 mname = *++tv; 2060 --maxatom; 2061 2062 /* extract host and user portions */ 2063 if (*++tv != NULL && (**tv & 0377) == CANONHOST) 2064 { 2065 hostp = ++tv; 2066 --maxatom; 2067 } 2068 else 2069 hostp = NULL; 2070 --maxatom; 2071 while (*tv != NULL && (**tv & 0377) != CANONUSER) 2072 { 2073 tv++; 2074 --maxatom; 2075 } 2076 if (*tv == NULL) 2077 { 2078 syserr("554 5.3.5 buildaddr: no user"); 2079 goto badaddr; 2080 } 2081 if (tv == hostp) 2082 hostp = NULL; 2083 else if (hostp != NULL) 2084 cataddr(hostp, tv - 1, hbuf, sizeof(hbuf), '\0', true); 2085 cataddr(++tv, NULL, ubuf, sizeof(ubuf), ' ', false); 2086 --maxatom; 2087 2088 /* save away the host name */ 2089 if (SM_STRCASEEQ(mname, "error")) 2090 { 2091 /* Set up triplet for use by -bv */ 2092 a->q_mailer = &errormailer; 2093 a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); 2094 /* XXX wrong place? */ 2095 2096 if (hostp != NULL) 2097 { 2098 register struct errcodes *ep; 2099 2100 a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); 2101 if (strchr(hbuf, '.') != NULL) 2102 { 2103 a->q_status = sm_rpool_strdup_x(e->e_rpool, 2104 hbuf); 2105 setstat(dsntoexitstat(hbuf)); 2106 } 2107 else if (isascii(hbuf[0]) && isdigit(hbuf[0])) 2108 { 2109 setstat(atoi(hbuf)); 2110 } 2111 else 2112 { 2113 for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 2114 if (SM_STRCASEEQ(ep->ec_name, hbuf)) 2115 break; 2116 setstat(ep->ec_code); 2117 } 2118 } 2119 else 2120 { 2121 a->q_host = NULL; 2122 setstat(EX_UNAVAILABLE); 2123 } 2124 stripquotes(ubuf); 2125 if (ISSMTPCODE(ubuf) && ubuf[3] == ' ') 2126 { 2127 char fmt[16]; 2128 int off; 2129 2130 if ((off = isenhsc(ubuf + 4, ' ')) > 0) 2131 { 2132 ubuf[off + 4] = '\0'; 2133 off += 5; 2134 } 2135 else 2136 { 2137 off = 4; 2138 ubuf[3] = '\0'; 2139 } 2140 (void) sm_strlcpyn(fmt, sizeof(fmt), 2, ubuf, " %s"); 2141 if (off > 4) 2142 usrerr(fmt, ubuf + off); 2143 else if (isenhsc(hbuf, '\0') > 0) 2144 usrerrenh(hbuf, fmt, ubuf + off); 2145 else 2146 usrerr(fmt, ubuf + off); 2147 /* XXX ubuf[off - 1] = ' '; */ 2148 if (ubuf[0] == '4') 2149 tempfail = true; 2150 } 2151 else 2152 { 2153 usrerr("553 5.3.0 %s", ubuf); 2154 } 2155 goto badaddr; 2156 } 2157 2158 for (mp = Mailer; (m = *mp++) != NULL; ) 2159 { 2160 if (SM_STRCASEEQ(m->m_name, mname)) 2161 break; 2162 } 2163 if (m == NULL) 2164 { 2165 syserr("554 5.3.5 buildaddr: unknown mailer %s", mname); 2166 goto badaddr; 2167 } 2168 a->q_mailer = m; 2169 2170 /* figure out what host (if any) */ 2171 if (hostp == NULL) 2172 { 2173 if (!bitnset(M_LOCALMAILER, m->m_flags)) 2174 { 2175 syserr("554 5.3.5 buildaddr: no host"); 2176 goto badaddr; 2177 } 2178 a->q_host = NULL; 2179 } 2180 else 2181 a->q_host = sm_rpool_strdup_x(e->e_rpool, hbuf); 2182 2183 /* figure out the user */ 2184 p = ubuf; 2185 if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@') 2186 { 2187 p++; 2188 tv++; 2189 --maxatom; 2190 a->q_flags |= QNOTREMOTE; 2191 } 2192 2193 /* do special mapping for local mailer */ 2194 if (*p == '"') 2195 p++; 2196 if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags)) 2197 a->q_mailer = m = ProgMailer; 2198 else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags)) 2199 a->q_mailer = m = FileMailer; 2200 else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags)) 2201 { 2202 /* may be :include: */ 2203 stripquotes(ubuf); 2204 if (sm_strncasecmp(ubuf, ":include:", 9) == 0) 2205 { 2206 /* if :include:, don't need further rewriting */ 2207 a->q_mailer = m = InclMailer; 2208 a->q_user = sm_rpool_strdup_x(e->e_rpool, &ubuf[9]); 2209 return a; 2210 } 2211 } 2212 2213 /* rewrite according recipient mailer rewriting rules */ 2214 macdefine(&e->e_macro, A_PERM, 'h', a->q_host); 2215 2216 if (ConfigLevel >= 10 || 2217 !bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) 2218 { 2219 /* sender addresses done later */ 2220 (void) rewrite(tv, 2, 0, e, maxatom); 2221 if (m->m_re_rwset > 0) 2222 (void) rewrite(tv, m->m_re_rwset, 0, e, maxatom); 2223 } 2224 (void) rewrite(tv, 4, 0, e, maxatom); 2225 2226 /* save the result for the command line/RCPT argument */ 2227 cataddr(tv, NULL, ubuf, sizeof(ubuf), '\0', true); 2228 a->q_user = sm_rpool_strdup_x(e->e_rpool, ubuf); 2229 2230 /* 2231 ** Do mapping to lower case as requested by mailer 2232 */ 2233 2234 if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags)) 2235 makelower_a(&a->q_host, e->e_rpool); 2236 if (!bitnset(M_USR_UPPER, m->m_flags)) 2237 makelower_a(&a->q_user, e->e_rpool); 2238 2239 if (tTd(24, 6)) 2240 { 2241 sm_dprintf("buildaddr => "); 2242 printaddr(sm_debug_file(), a, false); 2243 } 2244 return a; 2245 } 2246 2247 /* 2248 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 2249 ** 2250 ** Parameters: 2251 ** pvp -- parameter vector to rebuild. [i] 2252 ** evp -- last parameter to include. Can be NULL to 2253 ** use entire pvp. 2254 ** buf -- buffer to build the string into. 2255 ** sz -- size of buf. 2256 ** spacesub -- the space separator character; 2257 ** '\0': SpaceSub. 2258 ** NOSPACESEP: no separator 2259 ** external -- convert to external form? 2260 ** (undo "meta quoting") 2261 ** 2262 ** Returns: 2263 ** none. 2264 ** 2265 ** Side Effects: 2266 ** Destroys buf. 2267 ** 2268 ** Notes: 2269 ** There are two formats for strings: internal and external. 2270 ** The external format is just an eight-bit clean string (no 2271 ** null bytes, everything else OK). The internal format can 2272 ** include sendmail metacharacters. The special character 2273 ** METAQUOTE essentially quotes the character following, stripping 2274 ** it of all special semantics. 2275 ** 2276 ** The cataddr routine needs to be aware of whether it is producing 2277 ** an internal or external form as output (it only takes internal 2278 ** form as input). 2279 */ 2280 2281 void 2282 cataddr(pvp, evp, buf, sz, spacesub, external) 2283 char **pvp; 2284 char **evp; 2285 char *buf; 2286 register int sz; 2287 int spacesub; 2288 bool external; 2289 { 2290 bool oatomtok, natomtok; 2291 char *p; 2292 2293 oatomtok = natomtok = false; 2294 if (tTd(59, 14)) 2295 { 2296 sm_dprintf("cataddr(%d) <==", external); 2297 printav(sm_debug_file(), pvp); 2298 } 2299 2300 if (sz <= 0) 2301 return; 2302 2303 if (spacesub == '\0') 2304 spacesub = SpaceSub; 2305 2306 if (pvp == NULL) 2307 { 2308 *buf = '\0'; 2309 return; 2310 } 2311 p = buf; 2312 sz -= 2; 2313 while (*pvp != NULL && sz > 0) 2314 { 2315 char *q; 2316 2317 natomtok = (IntTokenTab[**pvp & 0xff] == ATM); 2318 if (oatomtok && natomtok && spacesub != NOSPACESEP) 2319 { 2320 *p++ = spacesub; 2321 if (--sz <= 0) 2322 break; 2323 } 2324 for (q = *pvp; *q != '\0'; ) 2325 { 2326 int c; 2327 2328 if (--sz <= 0) 2329 break; 2330 *p++ = c = *q++; 2331 2332 /* 2333 ** If the current character (c) is METAQUOTE and we 2334 ** want the "external" form and the next character 2335 ** is not NUL, then overwrite METAQUOTE with that 2336 ** character (i.e., METAQUOTE ch is changed to ch). 2337 ** p[-1] is used because p is advanced (above). 2338 */ 2339 2340 if ((c & 0377) == METAQUOTE && external && *q != '\0') 2341 p[-1] = *q++; 2342 } 2343 if (sz <= 0) 2344 break; 2345 oatomtok = natomtok; 2346 if (pvp++ == evp) 2347 break; 2348 } 2349 2350 #if 0 2351 /* 2352 ** Silently truncate long strings: even though this doesn't 2353 ** seem like a good idea it is necessary because header checks 2354 ** send the whole header value to rscheck() and hence rewrite(). 2355 ** The latter however sometimes uses a "short" buffer (e.g., 2356 ** cbuf[MAXNAME + 1]) to call cataddr() which then triggers this 2357 ** error function. One possible fix to the problem is to pass 2358 ** flags to rscheck() and rewrite() to distinguish the various 2359 ** calls and only trigger the error if necessary. For now just 2360 ** undo the change from 8.13.0. 2361 */ 2362 2363 if (sz <= 0) 2364 usrerr("cataddr: string too long"); 2365 #endif 2366 *p = '\0'; 2367 2368 if (tTd(59, 14)) 2369 sm_dprintf(" cataddr => %s\n", str2prt(buf)); 2370 } 2371 2372 /* 2373 ** SAMEADDR -- Determine if two addresses are the same 2374 ** 2375 ** This is not just a straight comparison -- if the mailer doesn't 2376 ** care about the host we just ignore it, etc. 2377 ** 2378 ** Parameters: 2379 ** a, b -- pointers to the internal forms to compare. 2380 ** 2381 ** Returns: 2382 ** true -- they represent the same mailbox. 2383 ** false -- they don't. 2384 ** 2385 ** Side Effects: 2386 ** none. 2387 */ 2388 2389 bool 2390 sameaddr(a, b) 2391 register ADDRESS *a; 2392 register ADDRESS *b; 2393 { 2394 register ADDRESS *ca, *cb; 2395 2396 /* if they don't have the same mailer, forget it */ 2397 if (a->q_mailer != b->q_mailer) 2398 return false; 2399 2400 /* 2401 ** Addresses resolving to error mailer 2402 ** should not be considered identical 2403 */ 2404 2405 if (a->q_mailer == &errormailer) 2406 return false; 2407 2408 /* if the user isn't the same, we can drop out */ 2409 if (strcmp(a->q_user, b->q_user) != 0) 2410 return false; 2411 2412 /* do the required flags match? */ 2413 if (!ADDR_FLAGS_MATCH(a, b)) 2414 return false; 2415 2416 /* if we have good uids for both but they differ, these are different */ 2417 if (a->q_mailer == ProgMailer) 2418 { 2419 ca = getctladdr(a); 2420 cb = getctladdr(b); 2421 if (ca != NULL && cb != NULL && 2422 bitset(QGOODUID, ca->q_flags & cb->q_flags) && 2423 ca->q_uid != cb->q_uid) 2424 return false; 2425 } 2426 2427 /* otherwise compare hosts (but be careful for NULL ptrs) */ 2428 if (a->q_host == b->q_host) 2429 { 2430 /* probably both null pointers */ 2431 return true; 2432 } 2433 if (a->q_host == NULL || b->q_host == NULL) 2434 { 2435 /* only one is a null pointer */ 2436 return false; 2437 } 2438 if (strcmp(a->q_host, b->q_host) != 0) 2439 return false; 2440 2441 return true; 2442 } 2443 /* 2444 ** PRINTADDR -- print address (for debugging) 2445 ** 2446 ** Parameters: 2447 ** a -- the address to print 2448 ** follow -- follow the q_next chain. 2449 ** 2450 ** Returns: 2451 ** none. 2452 ** 2453 ** Side Effects: 2454 ** none. 2455 */ 2456 2457 struct qflags 2458 { 2459 char *qf_name; 2460 unsigned long qf_bit; 2461 }; 2462 2463 /* :'a,.s;^#define \(Q[A-Z]*\) .*; { "\1", \1 },; */ 2464 static struct qflags AddressFlags[] = 2465 { 2466 { "QGOODUID", QGOODUID }, 2467 { "QPRIMARY", QPRIMARY }, 2468 { "QNOTREMOTE", QNOTREMOTE }, 2469 { "QSELFREF", QSELFREF }, 2470 { "QBOGUSSHELL", QBOGUSSHELL }, 2471 { "QUNSAFEADDR", QUNSAFEADDR }, 2472 { "QPINGONSUCCESS", QPINGONSUCCESS }, 2473 { "QPINGONFAILURE", QPINGONFAILURE }, 2474 { "QPINGONDELAY", QPINGONDELAY }, 2475 { "QHASNOTIFY", QHASNOTIFY }, 2476 { "QRELAYED", QRELAYED }, 2477 { "QEXPANDED", QEXPANDED }, 2478 { "QDELIVERED", QDELIVERED }, 2479 { "QDELAYED", QDELAYED }, 2480 { "QTHISPASS", QTHISPASS }, 2481 { "QALIAS", QALIAS }, 2482 { "QBYTRACE", QBYTRACE }, 2483 { "QBYNDELAY", QBYNDELAY }, 2484 { "QBYNRELAY", QBYNRELAY }, 2485 { "QINTBCC", QINTBCC }, 2486 { "QDYNMAILER", QDYNMAILER }, 2487 { "QSECURE", QSECURE }, 2488 { "QTHISPASS", QTHISPASS }, 2489 { "QRCPTOK", QRCPTOK }, 2490 { "QQUEUED", QQUEUED }, 2491 { NULL, 0 } 2492 }; 2493 2494 void 2495 printaddr(fp, a, follow) 2496 SM_FILE_T *fp; 2497 register ADDRESS *a; 2498 bool follow; 2499 { 2500 register MAILER *m; 2501 MAILER pseudomailer; 2502 register struct qflags *qfp; 2503 bool firstone; 2504 2505 if (a == NULL) 2506 { 2507 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "[NULL]\n"); 2508 return; 2509 } 2510 2511 while (a != NULL) 2512 { 2513 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%p=", (void *)a); 2514 (void) sm_io_flush(fp, SM_TIME_DEFAULT); 2515 2516 /* find the mailer -- carefully */ 2517 m = a->q_mailer; 2518 if (m == NULL) 2519 { 2520 m = &pseudomailer; 2521 m->m_mno = -1; 2522 m->m_name = "NULL"; 2523 } 2524 2525 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2526 "%s:\n\tmailer %d (%s), host `%s'\n", 2527 a->q_paddr == NULL ? "<null>" : a->q_paddr, 2528 m->m_mno, m->m_name, 2529 a->q_host == NULL ? "<null>" : a->q_host); 2530 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2531 "\tuser `%s', ruser `%s'\n", 2532 a->q_user, 2533 a->q_ruser == NULL ? "<null>" : a->q_ruser); 2534 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tstate="); 2535 switch (a->q_state) 2536 { 2537 case QS_OK: 2538 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "OK"); 2539 break; 2540 2541 case QS_DONTSEND: 2542 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2543 "DONTSEND"); 2544 break; 2545 2546 case QS_BADADDR: 2547 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2548 "BADADDR"); 2549 break; 2550 2551 case QS_QUEUEUP: 2552 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2553 "QUEUEUP"); 2554 break; 2555 2556 case QS_RETRY: 2557 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "RETRY"); 2558 break; 2559 2560 case QS_SENT: 2561 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "SENT"); 2562 break; 2563 2564 case QS_VERIFIED: 2565 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2566 "VERIFIED"); 2567 break; 2568 2569 case QS_EXPANDED: 2570 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2571 "EXPANDED"); 2572 break; 2573 2574 case QS_SENDER: 2575 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2576 "SENDER"); 2577 break; 2578 2579 case QS_CLONED: 2580 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2581 "CLONED"); 2582 break; 2583 2584 case QS_DISCARDED: 2585 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2586 "DISCARDED"); 2587 break; 2588 2589 case QS_REPLACED: 2590 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2591 "REPLACED"); 2592 break; 2593 2594 case QS_REMOVED: 2595 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2596 "REMOVED"); 2597 break; 2598 2599 case QS_DUPLICATE: 2600 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2601 "DUPLICATE"); 2602 break; 2603 2604 case QS_INCLUDED: 2605 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2606 "INCLUDED"); 2607 break; 2608 2609 default: 2610 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2611 "%d", a->q_state); 2612 break; 2613 } 2614 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2615 ", next=%p, alias %p, uid %d, gid %d\n", 2616 (void *)a->q_next, (void *)a->q_alias, 2617 (int) a->q_uid, (int) a->q_gid); 2618 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\tflags=%lx<", 2619 a->q_flags); 2620 firstone = true; 2621 for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) 2622 { 2623 if (!bitset(qfp->qf_bit, a->q_flags)) 2624 continue; 2625 if (!firstone) 2626 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2627 ","); 2628 firstone = false; 2629 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", 2630 qfp->qf_name); 2631 } 2632 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, ">\n"); 2633 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2634 "\towner=%s, home=\"%s\", fullname=\"%s\"\n", 2635 a->q_owner == NULL ? "(none)" : a->q_owner, 2636 a->q_home == NULL ? "(none)" : a->q_home, 2637 a->q_fullname == NULL ? "(none)" : a->q_fullname); 2638 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2639 "\torcpt=\"%s\", statmta=%s, status=%s\n", 2640 a->q_orcpt == NULL ? "(none)" : a->q_orcpt, 2641 a->q_statmta == NULL ? "(none)" : a->q_statmta, 2642 a->q_status == NULL ? "(none)" : a->q_status); 2643 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2644 "\tfinalrcpt=\"%s\"\n", 2645 a->q_finalrcpt == NULL ? "(none)" : a->q_finalrcpt); 2646 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2647 "\trstatus=\"%s\"\n", 2648 a->q_rstatus == NULL ? "(none)" : a->q_rstatus); 2649 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, 2650 "\tstatdate=%s\n", 2651 a->q_statdate == 0 ? "(none)" : ctime(&a->q_statdate)); 2652 2653 if (!follow) 2654 return; 2655 a = a->q_next; 2656 } 2657 } 2658 /* 2659 ** EMPTYADDR -- return true if this address is empty (``<>'') 2660 ** 2661 ** Parameters: 2662 ** a -- pointer to the address 2663 ** 2664 ** Returns: 2665 ** true -- if this address is "empty" (i.e., no one should 2666 ** ever generate replies to it. 2667 ** false -- if it is a "regular" (read: replyable) address. 2668 */ 2669 2670 bool 2671 emptyaddr(a) 2672 register ADDRESS *a; 2673 { 2674 return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 || 2675 a->q_user == NULL || strcmp(a->q_user, "<>") == 0; 2676 } 2677 2678 /* 2679 ** REMOTENAME -- return the name relative to the current mailer 2680 ** 2681 ** Parameters: 2682 ** name -- the name to translate. [i] 2683 ** m -- the mailer that we want to do rewriting relative to. 2684 ** flags -- fine tune operations. 2685 ** pstat -- pointer to status word. 2686 ** e -- the current envelope. 2687 ** 2688 ** Returns: 2689 ** the text string representing this address relative to 2690 ** the receiving mailer. [i] 2691 ** 2692 ** Warnings: 2693 ** The text string returned is tucked away locally; 2694 ** copy it if you intend to save it. 2695 */ 2696 2697 char * 2698 remotename(name, m, flags, pstat, e) 2699 char *name; 2700 struct mailer *m; 2701 int flags; 2702 int *pstat; 2703 register ENVELOPE *e; 2704 { 2705 register char **pvp; 2706 char *SM_NONVOLATILE fancy; 2707 char *oldg; 2708 int rwset; 2709 static char buf[MAXNAME_I + 1]; 2710 char lbuf[MAXNAME_I + 1]; 2711 char pvpbuf[PSBUFSIZE]; 2712 char addrtype[4]; 2713 2714 if (tTd(12, 1)) 2715 { 2716 sm_dprintf("remotename("); 2717 xputs(sm_debug_file(), name); 2718 sm_dprintf(")\n"); 2719 } 2720 2721 /* don't do anything if we are tagging it as special */ 2722 if (bitset(RF_SENDERADDR, flags)) 2723 { 2724 rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset 2725 : m->m_se_rwset; 2726 addrtype[2] = 's'; 2727 } 2728 else 2729 { 2730 rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset 2731 : m->m_re_rwset; 2732 addrtype[2] = 'r'; 2733 } 2734 if (rwset < 0) 2735 return name; 2736 addrtype[1] = ' '; 2737 addrtype[3] = '\0'; 2738 addrtype[0] = bitset(RF_HEADERADDR, flags) ? 'h' : 'e'; 2739 macdefine(&e->e_macro, A_TEMP, macid("{addr_type}"), addrtype); 2740 2741 /* 2742 ** Do a heuristic crack of this name to extract any comment info. 2743 ** This will leave the name as a comment and a $g macro. 2744 */ 2745 2746 if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags)) 2747 fancy = "\201g"; 2748 else 2749 fancy = crackaddr(name, e); 2750 2751 /* 2752 ** Turn the name into canonical form. 2753 ** Normally this will be RFC 822 style, i.e., "user@domain". 2754 ** If this only resolves to "user", and the "C" flag is 2755 ** specified in the sending mailer, then the sender's 2756 ** domain will be appended. 2757 */ 2758 2759 pvp = prescan(name, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, false); 2760 if (pvp == NULL) 2761 return name; 2762 if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 2763 *pstat = EX_TEMPFAIL; 2764 if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) 2765 { 2766 /* append from domain to this address */ 2767 register char **pxp = pvp; 2768 int l = MAXATOM; /* size of buffer for pvp */ 2769 2770 /* see if there is an "@domain" in the current name */ 2771 while (*pxp != NULL && strcmp(*pxp, "@") != 0) 2772 { 2773 pxp++; 2774 --l; 2775 } 2776 if (*pxp == NULL) 2777 { 2778 /* no.... append the "@domain" from the sender */ 2779 register char **qxq = e->e_fromdomain; 2780 2781 while ((*pxp++ = *qxq++) != NULL) 2782 { 2783 if (--l <= 0) 2784 { 2785 *--pxp = NULL; 2786 usrerr("553 5.1.0 remotename: too many tokens"); 2787 *pstat = EX_UNAVAILABLE; 2788 break; 2789 } 2790 } 2791 if (REWRITE(pvp, 3, e) == EX_TEMPFAIL) 2792 *pstat = EX_TEMPFAIL; 2793 } 2794 } 2795 2796 /* 2797 ** Do more specific rewriting. 2798 ** Rewrite using ruleset 1 or 2 depending on whether this is 2799 ** a sender address or not. 2800 ** Then run it through any receiving-mailer-specific rulesets. 2801 */ 2802 2803 if (bitset(RF_SENDERADDR, flags)) 2804 { 2805 if (REWRITE(pvp, 1, e) == EX_TEMPFAIL) 2806 *pstat = EX_TEMPFAIL; 2807 } 2808 else 2809 { 2810 if (REWRITE(pvp, 2, e) == EX_TEMPFAIL) 2811 *pstat = EX_TEMPFAIL; 2812 } 2813 if (rwset > 0) 2814 { 2815 if (REWRITE(pvp, rwset, e) == EX_TEMPFAIL) 2816 *pstat = EX_TEMPFAIL; 2817 } 2818 2819 /* 2820 ** Do any final sanitation the address may require. 2821 ** This will normally be used to turn internal forms 2822 ** (e.g., user@host.LOCAL) into external form. This 2823 ** may be used as a default to the above rules. 2824 */ 2825 2826 if (REWRITE(pvp, 4, e) == EX_TEMPFAIL) 2827 *pstat = EX_TEMPFAIL; 2828 2829 /* 2830 ** Now restore the comment information we had at the beginning. 2831 */ 2832 2833 cataddr(pvp, NULL, lbuf, sizeof(lbuf), '\0', false); 2834 oldg = macget(&e->e_macro, 'g'); 2835 macset(&e->e_macro, 'g', lbuf); 2836 2837 SM_TRY 2838 /* need to make sure route-addrs have <angle brackets> */ 2839 if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') 2840 expand("<\201g>", buf, sizeof(buf), e); 2841 else 2842 expand(fancy, buf, sizeof(buf), e); 2843 SM_FINALLY 2844 macset(&e->e_macro, 'g', oldg); 2845 SM_END_TRY 2846 2847 if (tTd(12, 1)) 2848 { 2849 sm_dprintf("remotename => `"); 2850 xputs(sm_debug_file(), buf); 2851 sm_dprintf("', stat=%d\n", *pstat); 2852 } 2853 return buf; 2854 } 2855 /* 2856 ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 2857 ** 2858 ** Parameters: 2859 ** a -- the address to map (but just the user name part). 2860 ** sendq -- the sendq in which to install any replacement 2861 ** addresses. 2862 ** aliaslevel -- the alias nesting depth. 2863 ** e -- the envelope. 2864 ** 2865 ** Returns: 2866 ** none. 2867 */ 2868 2869 #define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\ 2870 Q_PINGFLAGS|QHASNOTIFY|\ 2871 QRELAYED|QEXPANDED|QDELIVERED|QDELAYED|\ 2872 QBYTRACE|QBYNDELAY|QBYNRELAY) 2873 2874 void 2875 maplocaluser(a, sendq, aliaslevel, e) 2876 register ADDRESS *a; 2877 ADDRESS **sendq; 2878 int aliaslevel; 2879 ENVELOPE *e; 2880 { 2881 register char **pvp; 2882 register ADDRESS *SM_NONVOLATILE a1 = NULL; 2883 char pvpbuf[PSBUFSIZE]; 2884 2885 if (tTd(29, 1)) 2886 { 2887 sm_dprintf("maplocaluser: "); 2888 printaddr(sm_debug_file(), a, false); 2889 } 2890 pvp = prescan(a->q_user, '\0', pvpbuf, sizeof(pvpbuf), NULL, NULL, 2891 false); 2892 if (pvp == NULL) 2893 { 2894 if (tTd(29, 9)) 2895 sm_dprintf("maplocaluser: cannot prescan %s\n", 2896 a->q_user); 2897 return; 2898 } 2899 2900 macdefine(&e->e_macro, A_PERM, 'h', a->q_host); 2901 macdefine(&e->e_macro, A_PERM, 'u', a->q_user); 2902 macdefine(&e->e_macro, A_PERM, 'z', a->q_home); 2903 2904 macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e r"); 2905 if (REWRITE(pvp, 5, e) == EX_TEMPFAIL) 2906 { 2907 if (tTd(29, 9)) 2908 sm_dprintf("maplocaluser: rewrite tempfail\n"); 2909 a->q_state = QS_QUEUEUP; 2910 a->q_status = "4.4.3"; 2911 return; 2912 } 2913 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 2914 { 2915 if (tTd(29, 9)) 2916 sm_dprintf("maplocaluser: doesn't resolve\n"); 2917 return; 2918 } 2919 2920 SM_TRY 2921 a1 = buildaddr(pvp, NULL, 0, e); 2922 SM_EXCEPT(exc, "E:mta.quickabort") 2923 2924 /* 2925 ** mark address as bad, S5 returned an error 2926 ** and we gave that back to the SMTP client. 2927 */ 2928 2929 a->q_state = QS_DONTSEND; 2930 sm_exc_raisenew_x(&EtypeQuickAbort, 2); 2931 SM_END_TRY 2932 2933 /* if non-null, mailer destination specified -- has it changed? */ 2934 if (a1 == NULL || sameaddr(a, a1)) 2935 { 2936 if (tTd(29, 9)) 2937 sm_dprintf("maplocaluser: address unchanged\n"); 2938 return; 2939 } 2940 2941 /* make new address take on flags and print attributes of old */ 2942 a1->q_flags &= ~Q_COPYFLAGS; 2943 a1->q_flags |= a->q_flags & Q_COPYFLAGS; 2944 a1->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_paddr); 2945 a1->q_finalrcpt = a->q_finalrcpt; 2946 a1->q_orcpt = a->q_orcpt; 2947 2948 /* mark old address as dead; insert new address */ 2949 a->q_state = QS_REPLACED; 2950 if (tTd(29, 5)) 2951 { 2952 sm_dprintf("maplocaluser: QS_REPLACED "); 2953 printaddr(sm_debug_file(), a, false); 2954 } 2955 a1->q_alias = a; 2956 allocaddr(a1, RF_COPYALL, sm_rpool_strdup_x(e->e_rpool, a->q_paddr), e); 2957 (void) recipient(a1, sendq, aliaslevel, e); 2958 } 2959 /* 2960 ** DEQUOTE_INIT -- initialize dequote map 2961 ** 2962 ** Parameters: 2963 ** map -- the internal map structure. 2964 ** args -- arguments. 2965 ** 2966 ** Returns: 2967 ** true. 2968 */ 2969 2970 bool 2971 dequote_init(map, args) 2972 MAP *map; 2973 char *args; 2974 { 2975 register char *p = args; 2976 2977 /* there is no check whether there is really an argument */ 2978 map->map_mflags |= MF_KEEPQUOTES; 2979 for (;;) 2980 { 2981 while (SM_ISSPACE(*p)) 2982 p++; 2983 if (*p != '-') 2984 break; 2985 switch (*++p) 2986 { 2987 case 'a': 2988 map->map_app = ++p; 2989 break; 2990 2991 case 'D': 2992 map->map_mflags |= MF_DEFER; 2993 break; 2994 2995 case 'S': 2996 case 's': 2997 map->map_spacesub = *++p; 2998 break; 2999 } 3000 while (*p != '\0' && !(SM_ISSPACE(*p))) 3001 p++; 3002 if (*p != '\0') 3003 *p = '\0'; 3004 } 3005 if (map->map_app != NULL) 3006 map->map_app = newstr(map->map_app); 3007 3008 return true; 3009 } 3010 /* 3011 ** DEQUOTE_MAP -- unquote an address 3012 ** 3013 ** Parameters: 3014 ** map -- the internal map structure (ignored). 3015 ** name -- the name to dequote. 3016 ** av -- arguments (ignored). 3017 ** statp -- pointer to status out-parameter. 3018 ** 3019 ** Returns: 3020 ** NULL -- if there were no quotes, or if the resulting 3021 ** unquoted buffer would not be acceptable to prescan. 3022 ** else -- The dequoted buffer. 3023 */ 3024 3025 /* ARGSUSED2 */ 3026 char * 3027 dequote_map(map, name, av, statp) 3028 MAP *map; 3029 char *name; 3030 char **av; 3031 int *statp; 3032 { 3033 register char *p; 3034 register char *q; 3035 register char c; 3036 int anglecnt = 0; 3037 int cmntcnt = 0; 3038 int quotecnt = 0; 3039 int spacecnt = 0; 3040 bool quotemode = false; 3041 bool bslashmode = false; 3042 char spacesub = map->map_spacesub; 3043 3044 for (p = q = name; (c = *p++) != '\0'; ) 3045 { 3046 if (bslashmode) 3047 { 3048 bslashmode = false; 3049 *q++ = c; 3050 continue; 3051 } 3052 3053 if (c == ' ' && spacesub != '\0') 3054 c = spacesub; 3055 3056 switch (c) 3057 { 3058 case '\\': 3059 bslashmode = true; 3060 break; 3061 3062 case '(': 3063 cmntcnt++; 3064 break; 3065 3066 case ')': 3067 if (cmntcnt-- <= 0) 3068 return NULL; 3069 break; 3070 3071 case ' ': 3072 case '\t': 3073 spacecnt++; 3074 break; 3075 } 3076 3077 if (cmntcnt > 0) 3078 { 3079 *q++ = c; 3080 continue; 3081 } 3082 3083 switch (c) 3084 { 3085 case '"': 3086 quotemode = !quotemode; 3087 quotecnt++; 3088 continue; 3089 3090 case '<': 3091 anglecnt++; 3092 break; 3093 3094 case '>': 3095 if (anglecnt-- <= 0) 3096 return NULL; 3097 break; 3098 } 3099 *q++ = c; 3100 } 3101 3102 if (anglecnt != 0 || cmntcnt != 0 || bslashmode || 3103 quotemode || quotecnt <= 0 || spacecnt != 0) 3104 return NULL; 3105 *q++ = '\0'; 3106 return map_rewrite(map, name, strlen(name), NULL); 3107 } 3108 /* 3109 ** RSCHECK -- check string(s) for validity using rewriting sets 3110 ** 3111 ** Parameters: 3112 ** rwset -- the rewriting set to use. 3113 ** p1 -- the first string to check. 3114 ** p2 -- the second string to check -- may be NULL. 3115 ** e -- the current envelope. 3116 ** flags -- control some behavior, see RSF_ in sendmail.h 3117 ** logl -- logging level. 3118 ** host -- NULL or relay host. 3119 ** logid -- id for sm_syslog. 3120 ** addr -- if not NULL and ruleset returns $#error: 3121 ** store mailer triple here. 3122 ** addrstr -- if not NULL and ruleset does not return $#: 3123 ** address string 3124 ** 3125 ** Returns: 3126 ** EX_OK -- if the rwset doesn't resolve to $#error 3127 ** or is not defined 3128 ** else -- the failure status (message printed) 3129 */ 3130 3131 int 3132 rscheck(rwset, p1, p2, e, flags, logl, host, logid, addr, addrstr) 3133 char *rwset; 3134 const char *p1; 3135 const char *p2; 3136 ENVELOPE *e; 3137 int flags; 3138 int logl; 3139 const char *host; 3140 const char *logid; 3141 ADDRESS *addr; 3142 char **addrstr; 3143 { 3144 char *volatile buf; 3145 size_t bufsize; 3146 int saveexitstat; 3147 int volatile rstat = EX_OK; 3148 char **pvp; 3149 int rsno; 3150 bool volatile discard = false; 3151 bool saveQuickAbort = QuickAbort; 3152 bool saveSuprErrs = SuprErrs; 3153 bool quarantine = false; 3154 char ubuf[BUFSIZ * 2]; 3155 char buf0[MAXLINE]; 3156 char pvpbuf[PSBUFSIZE]; 3157 extern char MsgBuf[]; 3158 3159 if (tTd(48, 2)) 3160 sm_dprintf("rscheck(%s, %s, %s)\n", rwset, p1, 3161 p2 == NULL ? "(NULL)" : p2); 3162 3163 rsno = strtorwset(rwset, NULL, ST_FIND); 3164 if (rsno < 0) 3165 return EX_OK; 3166 3167 if (p2 != NULL) 3168 { 3169 bufsize = strlen(p1) + strlen(p2) + 2; 3170 if (bufsize > sizeof(buf0)) 3171 buf = sm_malloc_x(bufsize); 3172 else 3173 { 3174 buf = buf0; 3175 bufsize = sizeof(buf0); 3176 } 3177 (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); 3178 } 3179 else 3180 { 3181 bufsize = strlen(p1) + 1; 3182 if (bufsize > sizeof(buf0)) 3183 buf = sm_malloc_x(bufsize); 3184 else 3185 { 3186 buf = buf0; 3187 bufsize = sizeof(buf0); 3188 } 3189 (void) sm_strlcpy(buf, p1, bufsize); 3190 } 3191 SM_TRY 3192 { 3193 SuprErrs = true; 3194 QuickAbort = false; 3195 pvp = prescan(buf, '\0', pvpbuf, sizeof(pvpbuf), NULL, 3196 bitset(RSF_RMCOMM, flags) ? 3197 IntTokenTab : TokTypeNoC, 3198 bitset(RSF_RMCOMM, flags) ? false : true); 3199 SuprErrs = saveSuprErrs; 3200 if (pvp == NULL) 3201 { 3202 if (tTd(48, 2)) 3203 sm_dprintf("rscheck: cannot prescan input\n"); 3204 /* 3205 syserr("rscheck: cannot prescan input: \"%s\"", 3206 shortenstring(buf, MAXSHORTSTR)); 3207 rstat = EX_DATAERR; 3208 */ 3209 goto finis; 3210 } 3211 if (bitset(RSF_UNSTRUCTURED, flags)) 3212 SuprErrs = true; 3213 (void) REWRITE(pvp, rsno, e); 3214 if (bitset(RSF_UNSTRUCTURED, flags)) 3215 SuprErrs = saveSuprErrs; 3216 3217 if (pvp[0] != NULL && (pvp[0][0] & 0377) != CANONNET && 3218 bitset(RSF_ADDR, flags) && addrstr != NULL) 3219 { 3220 cataddr(&(pvp[0]), NULL, ubuf, sizeof(ubuf), 3221 bitset(RSF_STRING, flags) ? NOSPACESEP : ' ', 3222 true); 3223 *addrstr = sm_rpool_strdup_x(e->e_rpool, ubuf); 3224 goto finis; 3225 } 3226 3227 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || 3228 pvp[1] == NULL || (strcmp(pvp[1], "error") != 0 && 3229 strcmp(pvp[1], "discard") != 0)) 3230 { 3231 goto finis; 3232 } 3233 3234 if (strcmp(pvp[1], "discard") == 0) 3235 { 3236 if (tTd(48, 2)) 3237 sm_dprintf("rscheck: discard mailer selected\n"); 3238 e->e_flags |= EF_DISCARD; 3239 discard = true; 3240 } 3241 else if (strcmp(pvp[1], "error") == 0 && 3242 pvp[2] != NULL && (pvp[2][0] & 0377) == CANONHOST && 3243 pvp[3] != NULL && strcmp(pvp[3], "quarantine") == 0) 3244 { 3245 if (pvp[4] == NULL || 3246 (pvp[4][0] & 0377) != CANONUSER || 3247 pvp[5] == NULL) 3248 e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 3249 rwset); 3250 else 3251 { 3252 cataddr(&(pvp[5]), NULL, ubuf, 3253 sizeof(ubuf), ' ', true); 3254 e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 3255 ubuf); 3256 } 3257 macdefine(&e->e_macro, A_PERM, 3258 macid("{quarantine}"), e->e_quarmsg); 3259 quarantine = true; 3260 } 3261 else 3262 { 3263 auto ADDRESS a1; 3264 int savelogusrerrs = LogUsrErrs; 3265 static bool logged = false; 3266 3267 /* got an error -- process it */ 3268 saveexitstat = ExitStat; 3269 LogUsrErrs = false; 3270 (void) buildaddr(pvp, &a1, 0, e); 3271 if (addr != NULL) 3272 { 3273 addr->q_mailer = a1.q_mailer; 3274 addr->q_user = a1.q_user; 3275 addr->q_host = a1.q_host; 3276 } 3277 LogUsrErrs = savelogusrerrs; 3278 rstat = ExitStat; 3279 ExitStat = saveexitstat; 3280 if (!logged) 3281 { 3282 if (bitset(RSF_COUNT, flags)) 3283 markstats(e, &a1, STATS_REJECT); 3284 logged = true; 3285 } 3286 } 3287 3288 if (LogLevel > logl) 3289 { 3290 const char *relay; 3291 char *p; 3292 char lbuf[MAXLINE]; 3293 3294 p = lbuf; 3295 if (p2 != NULL) 3296 { 3297 (void) sm_snprintf(p, SPACELEFT(lbuf, p), 3298 ", arg2=%s", 3299 p2); 3300 p += strlen(p); 3301 } 3302 3303 if (host != NULL) 3304 relay = host; 3305 else 3306 relay = macvalue('_', e); 3307 if (relay != NULL) 3308 { 3309 (void) sm_snprintf(p, SPACELEFT(lbuf, p), 3310 ", relay=%s", relay); 3311 p += strlen(p); 3312 } 3313 *p = '\0'; 3314 if (discard) 3315 sm_syslog(LOG_NOTICE, logid, 3316 "ruleset=%s, arg1=%s%s, discard", 3317 rwset, p1, lbuf); 3318 else if (quarantine) 3319 sm_syslog(LOG_NOTICE, logid, 3320 "ruleset=%s, arg1=%s%s, quarantine=%s", 3321 rwset, p1, lbuf, ubuf); 3322 else 3323 sm_syslog(LOG_NOTICE, logid, 3324 "ruleset=%s, arg1=%s%s, reject=%s", 3325 rwset, p1, lbuf, MsgBuf); 3326 } 3327 3328 finis: ; 3329 } 3330 SM_FINALLY 3331 { 3332 /* clean up */ 3333 if (buf != buf0) 3334 sm_free(buf); 3335 QuickAbort = saveQuickAbort; 3336 } 3337 SM_END_TRY 3338 3339 setstat(rstat); 3340 3341 /* rulesets don't set errno */ 3342 errno = 0; 3343 if (rstat != EX_OK && QuickAbort) 3344 sm_exc_raisenew_x(&EtypeQuickAbort, 2); 3345 return rstat; 3346 } 3347 3348 /* 3349 ** RSCAP -- call rewriting set to return capabilities 3350 ** 3351 ** Parameters: 3352 ** rwset -- the rewriting set to use. 3353 ** p1 -- the first string to check. 3354 ** p2 -- the second string to check -- may be NULL. 3355 ** e -- the current envelope. 3356 ** pvp -- pointer to token vector. 3357 ** pvpbuf -- buffer space. 3358 ** size -- size of buffer space. 3359 ** 3360 ** Returns: 3361 ** EX_UNAVAILABLE -- ruleset doesn't exist. 3362 ** EX_DATAERR -- prescan() failed. 3363 ** EX_OK -- rewrite() was successful. 3364 ** else -- return status from rewrite(). 3365 */ 3366 3367 int 3368 rscap(rwset, p1, p2, e, pvp, pvpbuf, size) 3369 char *rwset; 3370 char *p1; 3371 char *p2; 3372 ENVELOPE *e; 3373 char ***pvp; 3374 char *pvpbuf; 3375 int size; 3376 { 3377 char *volatile buf; 3378 size_t bufsize; 3379 int volatile rstat = EX_OK; 3380 int rsno; 3381 bool saveQuickAbort = QuickAbort; 3382 bool saveSuprErrs = SuprErrs; 3383 char buf0[MAXLINE]; 3384 extern char MsgBuf[]; 3385 3386 if (tTd(48, 2)) 3387 sm_dprintf("rscap(%s, %s, %s)\n", rwset, p1, 3388 p2 == NULL ? "(NULL)" : p2); 3389 3390 SM_REQUIRE(pvp != NULL); 3391 rsno = strtorwset(rwset, NULL, ST_FIND); 3392 if (rsno < 0) 3393 return EX_UNAVAILABLE; 3394 3395 if (p2 != NULL) 3396 { 3397 bufsize = strlen(p1) + strlen(p2) + 2; 3398 if (bufsize > sizeof(buf0)) 3399 buf = sm_malloc_x(bufsize); 3400 else 3401 { 3402 buf = buf0; 3403 bufsize = sizeof(buf0); 3404 } 3405 (void) sm_snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); 3406 } 3407 else 3408 { 3409 bufsize = strlen(p1) + 1; 3410 if (bufsize > sizeof(buf0)) 3411 buf = sm_malloc_x(bufsize); 3412 else 3413 { 3414 buf = buf0; 3415 bufsize = sizeof(buf0); 3416 } 3417 (void) sm_strlcpy(buf, p1, bufsize); 3418 } 3419 SM_TRY 3420 { 3421 SuprErrs = true; 3422 QuickAbort = false; 3423 *pvp = prescan(buf, '\0', pvpbuf, size, NULL, IntTokenTab, 3424 false); 3425 if (*pvp != NULL) 3426 rstat = rewrite(*pvp, rsno, 0, e, size); 3427 else 3428 { 3429 if (tTd(48, 2)) 3430 sm_dprintf("rscap: cannot prescan input\n"); 3431 rstat = EX_DATAERR; 3432 } 3433 } 3434 SM_FINALLY 3435 { 3436 /* clean up */ 3437 if (buf != buf0) 3438 sm_free(buf); 3439 SuprErrs = saveSuprErrs; 3440 QuickAbort = saveQuickAbort; 3441 3442 /* prevent information leak, this may contain rewrite error */ 3443 MsgBuf[0] = '\0'; 3444 } 3445 SM_END_TRY 3446 return rstat; 3447 } 3448