qmail-local.c (19012B)
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <stdlib.h> 4 #include "readwrite.h" 5 #include "sig.h" 6 #include "env.h" 7 #include "byte.h" 8 #include "datetime.h" 9 #include "exit.h" 10 #include "fork.h" 11 #include "open.h" 12 #include "wait.h" 13 #include "lock.h" 14 #include "seek.h" 15 #include "substdio.h" 16 #include "getln.h" 17 #include "strerr.h" 18 #include "subfd.h" 19 #include "sgetopt.h" 20 #include "alloc.h" 21 #include "error.h" 22 #include "stralloc.h" 23 #include "fmt.h" 24 #include "str.h" 25 #include "now.h" 26 #include "case.h" 27 #include "quote.h" 28 #include "qmail.h" 29 #include "slurpclose.h" 30 #include "myctime.h" 31 #include "gfrom.h" 32 #include "auto_patrn.h" 33 34 void _noreturn_ usage() { 35 strerr_die1x(100, "qmail-local: usage: qmail-local [ -nNT ] user homedir local dash ext domain sender aliasempty"); 36 } 37 38 void _noreturn_ temp_nomem() { 39 strerr_die1x(111, "4.3.0 Out of memory."); 40 } 41 void _noreturn_ temp_rewind() { 42 strerr_die1x(111, "4.3.0 Unable to rewind message."); 43 } 44 void _noreturn_ temp_childcrashed() { 45 strerr_die1x(111, "4.3.0 Aack, child crashed."); 46 } 47 void _noreturn_ temp_fork() { 48 strerr_die3x(111, "4.3.0 Unable to fork: ", error_str(errno), "."); 49 } 50 void _noreturn_ temp_read() { 51 strerr_die3x(111, "4.3.0 Unable to read message: ", error_str(errno), "."); 52 } 53 void _noreturn_ 54 temp_slowlock() 55 { 56 strerr_die1x(111, "4.3.0 File has been locked for 30 seconds straight."); 57 } 58 void _noreturn_ 59 temp_qmail(char *fn) 60 { 61 strerr_die5x(111, "4.3.0 Unable to open ", fn, ": ", error_str(errno), " ."); 62 } 63 64 int flagdoit = 0, flagprobe = 0; 65 int flag99; 66 67 char *user; 68 char *homedir; 69 char *local; 70 char *dash; 71 char *ext; 72 char *host; 73 char *sender; 74 char *aliasempty; 75 76 stralloc safeext = {0}; 77 stralloc ufline = {0}; 78 stralloc rpline = {0}; 79 stralloc envrecip = {0}; 80 stralloc dtline = {0}; 81 stralloc qme = {0}; 82 stralloc ueo = {0}; 83 stralloc cmds = {0}; 84 stralloc messline = {0}; 85 stralloc foo = {0}; 86 87 char buf[1024]; 88 char outbuf[1024]; 89 90 /* child process */ 91 92 char fntmptph[80 + FMT_ULONG * 2]; 93 char fnnewtph[80 + FMT_ULONG * 2]; 94 void tryunlinktmp() { 95 unlink(fntmptph); 96 } 97 void sigalrm() { 98 tryunlinktmp(); 99 _exit(3); 100 } 101 102 void 103 maildir_child(char *dir) 104 { 105 unsigned long pid; 106 unsigned long time; 107 char myhost[64]; 108 char *s; 109 int loop; 110 int fd; 111 substdio ss; 112 substdio ssout; 113 114 sig_alarmcatch(sigalrm); 115 if (chdir(dir) == -1) { 116 if (error_temp(errno)) 117 _exit(1); 118 _exit(2); 119 } 120 pid = getpid(); 121 myhost[0] = 0; 122 gethostname(myhost, sizeof(myhost)); 123 for (loop = 0;; ++loop) { 124 time = now(); 125 s = fntmptph; 126 s += fmt_str(s, "tmp/"); 127 s += fmt_ulong(s, time); 128 *s++ = '.'; 129 s += fmt_ulong(s, pid); 130 *s++ = '.'; 131 s += fmt_strn(s, myhost, sizeof(myhost)); 132 *s++ = 0; 133 alarm(86400); 134 fd = open_excl(fntmptph); 135 if (fd >= 0) 136 break; 137 if (errno == error_exist) { 138 /* really should never get to this point */ 139 if (loop == 2) 140 _exit(1); 141 sleep(2); 142 } else { 143 _exit(1); 144 } 145 } 146 str_copy(fnnewtph, fntmptph); 147 byte_copy(fnnewtph, 3, "new"); 148 149 substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); 150 substdio_fdbuf(&ssout, write, fd, outbuf, sizeof(outbuf)); 151 if (substdio_put(&ssout, rpline.s, rpline.len) == -1) 152 goto fail; 153 if (substdio_put(&ssout, dtline.s, dtline.len) == -1) 154 goto fail; 155 156 switch (substdio_copy(&ssout, &ss)) { 157 case -2: 158 tryunlinktmp(); 159 _exit(4); 160 case -3: 161 goto fail; 162 } 163 164 if (substdio_flush(&ssout) == -1) 165 goto fail; 166 if (fsync(fd) == -1) 167 goto fail; 168 if (close(fd) == -1) 169 goto fail; /* NFS dorks */ 170 171 if (link(fntmptph, fnnewtph) == -1) 172 goto fail; 173 /* if it was error_exist, almost certainly successful; i hate NFS */ 174 tryunlinktmp(); 175 _exit(0); 176 177 fail: tryunlinktmp(); 178 _exit(1); 179 } 180 181 /* end child process */ 182 183 void 184 maildir(char *fn) 185 { 186 int child; 187 int wstat; 188 189 if (seek_begin(0) == -1) 190 temp_rewind(); 191 192 switch (child = fork()) { 193 case -1: 194 temp_fork(); 195 case 0: 196 maildir_child(fn); 197 _exit(111); 198 } 199 200 wait_pid(&wstat, child); 201 if (wait_crashed(wstat)) 202 temp_childcrashed(); 203 switch (wait_exitcode(wstat)) { 204 case 0: 205 break; 206 case 2: 207 strerr_die1x(111, "4.2.1 Unable to chdir to maildir. "); 208 case 3: 209 strerr_die1x(111, "4.3.0 Timeout on maildir delivery. "); 210 case 4: 211 strerr_die1x(111, "4.3.0 Unable to read message. "); 212 default: 213 strerr_die1x(111, "4.3.0 Temporary error on maildir delivery. "); 214 } 215 } 216 217 void 218 mailfile(char *fn) 219 { 220 int fd; 221 substdio ss; 222 substdio ssout; 223 int match; 224 seek_pos pos; 225 int flaglocked; 226 227 if (seek_begin(0) == -1) 228 temp_rewind(); 229 230 fd = open_append(fn); 231 if (fd == -1) 232 strerr_die5x(111, "4.2.1 Unable to open ", fn, ": ", error_str(errno), "."); 233 234 sig_alarmcatch(temp_slowlock); 235 alarm(30); 236 flaglocked = (lock_ex(fd) != -1); 237 alarm(0); 238 sig_alarmdefault(); 239 240 seek_end(fd); 241 pos = seek_cur(fd); 242 243 substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); 244 substdio_fdbuf(&ssout, write, fd, outbuf, sizeof(outbuf)); 245 if (substdio_put(&ssout, ufline.s, ufline.len)) 246 goto writeerrs; 247 if (substdio_put(&ssout, rpline.s, rpline.len)) 248 goto writeerrs; 249 if (substdio_put(&ssout, dtline.s, dtline.len)) 250 goto writeerrs; 251 for (;;) { 252 if (getln(&ss, &messline, &match, '\n') != 0) { 253 strerr_warn3("4.3.0 Unable to read message: ", error_str(errno), ".", 0); 254 if (flaglocked) 255 seek_trunc(fd, pos); 256 close(fd); 257 _exit(111); 258 } 259 if (!match && !messline.len) 260 break; 261 if (gfrom(messline.s, messline.len)) 262 if (substdio_bput(&ssout, ">", 1)) 263 goto writeerrs; 264 if (substdio_bput(&ssout, messline.s, messline.len)) 265 goto writeerrs; 266 if (!match) { 267 if (substdio_bputs(&ssout, "\n")) 268 goto writeerrs; 269 break; 270 } 271 } 272 if (substdio_bputs(&ssout, "\n")) 273 goto writeerrs; 274 if (substdio_flush(&ssout)) 275 goto writeerrs; 276 if (fsync(fd) == -1) 277 goto writeerrs; 278 close(fd); 279 return; 280 281 writeerrs: 282 strerr_warn5("Unable to write ", fn, ": ", error_str(errno), "4.3.0 . ", 0); 283 if (flaglocked) 284 seek_trunc(fd, pos); 285 close(fd); 286 _exit(111); 287 } 288 289 void 290 mailprogram(char *prog) 291 { 292 int child; 293 char *(args[4]); 294 int wstat; 295 296 if (seek_begin(0) == -1) 297 temp_rewind(); 298 299 /* About here is where you'd add the logic for Courieresque pipe-pipe deliveries. */ 300 301 switch (child = fork()) { 302 case -1: 303 temp_fork(); 304 case 0: 305 /* MXF XXX: Should add a control file, maybe with a per user override?, to allow this to be execlineb, instead. */ 306 args[0] = "/bin/sh"; 307 args[1] = "-c"; 308 args[2] = prog; 309 args[3] = 0; 310 sig_pipedefault(); 311 execv(*args, args); 312 strerr_die3x(111, "4.3.0 Unable to run /bin/sh: ", error_str(errno), "."); 313 } 314 315 wait_pid(&wstat, child); 316 if (wait_crashed(wstat)) 317 temp_childcrashed(); 318 switch (wait_exitcode(wstat)) { 319 case 100: 320 case 64: 321 case 65: 322 case 70: 323 case 76: 324 case 77: 325 case 78: 326 case 112: 327 _exit(100); 328 case 0: 329 break; 330 case 99: 331 flag99 = 1; 332 break; 333 default: 334 _exit(111); 335 } 336 } 337 338 unsigned long mailforward_qp = 0; 339 340 void 341 mailforward(char **recips) 342 { 343 struct qmail qqt; 344 char *qqx; 345 substdio ss; 346 int match; 347 348 if (seek_begin(0) == -1) 349 temp_rewind(); 350 substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); 351 352 if (qmail_open(&qqt) == -1) 353 temp_fork(); 354 mailforward_qp = qmail_qp(&qqt); 355 qmail_put(&qqt, dtline.s, dtline.len); 356 do { 357 if (getln(&ss, &messline, &match, '\n') != 0) { 358 qmail_fail(&qqt); 359 break; 360 } 361 qmail_put(&qqt, messline.s, messline.len); 362 } 363 while (match); 364 qmail_from(&qqt, ueo.s); 365 while (*recips) 366 qmail_to(&qqt, *recips++); 367 qqx = qmail_close(&qqt); 368 if (!*qqx) 369 return; 370 strerr_die3x(*qqx == 'D' ? 100 : 111, "Unable to forward message: ", qqx + 1, "."); 371 } 372 373 void 374 bouncexf() 375 { 376 int match; 377 substdio ss; 378 379 if (seek_begin(0) == -1) 380 temp_rewind(); 381 substdio_fdbuf(&ss, read, 0, buf, sizeof(buf)); 382 for (;;) { 383 if (getln(&ss, &messline, &match, '\n') != 0) 384 temp_read(); 385 if (!match) 386 break; 387 if (messline.len <= 1) 388 break; 389 if (messline.len == dtline.len) 390 if (!str_diffn(messline.s, dtline.s, dtline.len)) 391 strerr_die1x(100, "5.4.6 This message is looping: it already has my Delivered-To line. "); 392 } 393 } 394 395 void 396 checkhome() 397 { 398 struct stat st; 399 400 if (stat(".", &st) == -1) 401 strerr_die3x(111, "Unable to stat home directory: ", error_str(errno), "4.3.0 . "); 402 if (st.st_mode & auto_patrn) 403 strerr_die1x(111, "4.7.0 Uh-oh: home directory is writable. "); 404 if (st.st_mode & 01000) { 405 if (flagdoit) 406 strerr_die1x(111, "4.2.1 Home directory is sticky: user is editing their .qmail file. "); 407 else 408 strerr_warn1("Warning: home directory is sticky.", 0); 409 } 410 } 411 412 int 413 qmeox(dashowner) 414 char *dashowner; 415 { 416 struct stat st; 417 418 if (!stralloc_copys(&qme, ".qmail")) 419 temp_nomem(); 420 if (!stralloc_cats(&qme, dash)) 421 temp_nomem(); 422 if (!stralloc_cat(&qme, &safeext)) 423 temp_nomem(); 424 if (!stralloc_cats(&qme, dashowner)) 425 temp_nomem(); 426 if (!stralloc_0(&qme)) 427 temp_nomem(); 428 if (stat(qme.s, &st) == -1) { 429 if (error_temp(errno)) 430 temp_qmail(qme.s); 431 return -1; 432 } 433 return 0; 434 } 435 436 /* MXF NOTES: int *cutable == "PBR flag executable" 437 */ 438 int 439 qmeexists(int *fd, int *cutable) 440 { 441 struct stat st; 442 443 if (!stralloc_0(&qme)) 444 temp_nomem(); 445 446 *fd = open_read(qme.s); 447 if (*fd == -1) { 448 if (error_temp(errno)) 449 temp_qmail(qme.s); 450 if (errno == error_perm) 451 temp_qmail(qme.s); 452 if (errno == error_acces) 453 temp_qmail(qme.s); 454 return 0; 455 } 456 457 if (fstat(*fd, &st) == -1) 458 temp_qmail(qme.s); 459 if ((st.st_mode & S_IFMT) == S_IFREG) { 460 if (st.st_mode & auto_patrn) 461 strerr_die1x(111, "4.7.0 Uh-oh: .qmail file is writable. "); 462 *cutable = !!(st.st_mode & 0100); 463 return 1; 464 } 465 close(*fd); 466 return 0; 467 } 468 469 /* "" "": "" */ 470 /* "-/" "": "-/" "-/default" */ 471 /* "-/" "a": "-/a" "-/default" */ 472 /* "-/" "a-": "-/a-" "-/a-default" "-/default" */ 473 /* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */ 474 /* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */ 475 /* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */ 476 /* MXF NOTE: notably, this does not support using custom dashes anywhere 477 * except the first split of the address. */ 478 void 479 qmesearch(int *fd, int *cutable) 480 { 481 int i; 482 483 if (!stralloc_copys(&qme, ".qmail")) 484 temp_nomem(); 485 if (!stralloc_cats(&qme, dash)) 486 temp_nomem(); 487 if (!stralloc_cat(&qme, &safeext)) 488 temp_nomem(); 489 if (qmeexists(fd, cutable)) { 490 if (safeext.len >= 7) { 491 i = safeext.len - 7; 492 if (byte_equal("default", 7, safeext.s + i)) 493 if (i <= str_len(ext)) /* paranoia */ 494 if (!env_put2("DEFAULT", ext + i)) 495 temp_nomem(); 496 } 497 return; 498 } 499 500 for (i = safeext.len; i >= 0; --i) 501 if (!i || (safeext.s[i - 1] == '-')) { 502 if (!stralloc_copys(&qme, ".qmail")) 503 temp_nomem(); 504 if (!stralloc_cats(&qme, dash)) 505 temp_nomem(); 506 if (!stralloc_catb(&qme, safeext.s, i)) 507 temp_nomem(); 508 if (!stralloc_cats(&qme, "default")) 509 temp_nomem(); 510 if (qmeexists(fd, cutable)) { 511 if (i <= str_len(ext)) /* paranoia */ 512 if (!env_put2("DEFAULT", ext + i)) 513 temp_nomem(); 514 return; 515 } 516 } 517 518 *fd = -1; 519 } 520 521 unsigned long count_file = 0; 522 unsigned long count_forward = 0; 523 unsigned long count_program = 0; 524 char count_buf[FMT_ULONG]; 525 526 void 527 count_print() 528 { 529 substdio_puts(subfdoutsmall, "did "); 530 substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, count_file)); 531 substdio_puts(subfdoutsmall, "+"); 532 substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, count_forward)); 533 substdio_puts(subfdoutsmall, "+"); 534 substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, count_program)); 535 substdio_puts(subfdoutsmall, "\n"); 536 if (mailforward_qp) { 537 substdio_puts(subfdoutsmall, "qp "); 538 substdio_put(subfdoutsmall, count_buf, fmt_ulong(count_buf, mailforward_qp)); 539 substdio_puts(subfdoutsmall, "\n"); 540 } 541 substdio_flush(subfdoutsmall); 542 } 543 544 void 545 sayit(char *type, char *cmd, unsigned int len) 546 { 547 substdio_puts(subfdoutsmall, type); 548 substdio_put(subfdoutsmall, cmd, len); 549 substdio_putsflush(subfdoutsmall, "\n"); 550 } 551 552 int 553 main(int argc, char **argv) 554 { 555 int opt; 556 unsigned int i; 557 unsigned int j; 558 int fd; 559 unsigned int numforward; 560 char **recips; 561 datetime_sec starttime; 562 int flagforwardonly; 563 char *x; 564 565 umask(077); 566 sig_pipeignore(); 567 568 if (!env_init()) 569 temp_nomem(); 570 571 flagdoit = 1; 572 while ((opt = getopt(argc, argv, "nNT")) != opteof) 573 switch (opt) { 574 case 'n': 575 flagdoit = 0; 576 break; 577 case 'N': 578 flagdoit = 1; 579 break; 580 case 'T': 581 flagdoit = 0; 582 flagprobe = 1; 583 break; 584 default: 585 usage(); 586 } 587 argc -= optind; 588 argv += optind; 589 590 if (!(user = *argv++)) 591 usage(); 592 if (!(homedir = *argv++)) 593 usage(); 594 if (!(local = *argv++)) 595 usage(); 596 if (!(dash = *argv++)) 597 usage(); 598 if (!(ext = *argv++)) 599 usage(); 600 if (!(host = *argv++)) 601 usage(); 602 if (!(sender = *argv++)) 603 usage(); 604 if (!(aliasempty = *argv++)) 605 usage(); 606 if (*argv) 607 usage(); 608 609 if (homedir[0] != '/') 610 usage(); 611 if (chdir(homedir) == -1) 612 strerr_die5x(111, "4.3.0 Unable to switch to ", homedir, ": ", error_str(errno), "."); 613 checkhome(); 614 615 if (!env_put2("HOST", host)) 616 temp_nomem(); 617 if (!env_put2("HOME", homedir)) 618 temp_nomem(); 619 if (!env_put2("USER", user)) 620 temp_nomem(); 621 if (!env_put2("LOCAL", local)) 622 temp_nomem(); 623 624 if (!stralloc_copys(&envrecip, local)) 625 temp_nomem(); 626 if (!stralloc_cats(&envrecip, "@")) 627 temp_nomem(); 628 if (!stralloc_cats(&envrecip, host)) 629 temp_nomem(); 630 631 if (!stralloc_copy(&foo, &envrecip)) 632 temp_nomem(); 633 if (!stralloc_0(&foo)) 634 temp_nomem(); 635 if (!env_put2("RECIPIENT", foo.s)) 636 temp_nomem(); 637 638 if (!stralloc_copys(&dtline, "Delivered-To: ")) 639 temp_nomem(); 640 if (!stralloc_cat(&dtline, &envrecip)) 641 temp_nomem(); 642 for (i = 0; i < dtline.len; ++i) 643 if (dtline.s[i] == '\n') 644 dtline.s[i] = '_'; 645 if (!stralloc_cats(&dtline, "\n")) 646 temp_nomem(); 647 648 if (!stralloc_copy(&foo, &dtline)) 649 temp_nomem(); 650 if (!stralloc_0(&foo)) 651 temp_nomem(); 652 if (!env_put2("DTLINE", foo.s)) 653 temp_nomem(); 654 655 if (flagdoit) 656 bouncexf(); 657 658 if (!env_put2("SENDER", sender)) 659 temp_nomem(); 660 661 if (!quote2(&foo, sender)) 662 temp_nomem(); 663 if (!stralloc_copys(&rpline, "Return-Path: <")) 664 temp_nomem(); 665 if (!stralloc_cat(&rpline, &foo)) 666 temp_nomem(); 667 for (i = 0; i < rpline.len; ++i) 668 if (rpline.s[i] == '\n') 669 rpline.s[i] = '_'; 670 if (!stralloc_cats(&rpline, ">\n")) 671 temp_nomem(); 672 673 if (!stralloc_copy(&foo, &rpline)) 674 temp_nomem(); 675 if (!stralloc_0(&foo)) 676 temp_nomem(); 677 if (!env_put2("RPLINE", foo.s)) 678 temp_nomem(); 679 680 if (!stralloc_copys(&ufline, "From ")) 681 temp_nomem(); 682 if (*sender) { 683 unsigned int len; 684 char ch; 685 686 len = str_len(sender); 687 if (!stralloc_readyplus(&ufline, len)) 688 temp_nomem(); 689 for (i = 0; i < len; ++i) { 690 ch = sender[i]; 691 if ((ch == ' ') || (ch == '\t') || (ch == '\n')) 692 ch = '-'; 693 ufline.s[ufline.len + i] = ch; 694 } 695 ufline.len += len; 696 } else if (!stralloc_cats(&ufline, "MAILER-DAEMON")) 697 temp_nomem(); 698 if (!stralloc_cats(&ufline, " ")) 699 temp_nomem(); 700 starttime = now(); 701 if (!stralloc_cats(&ufline, myctime(starttime))) 702 temp_nomem(); 703 704 if (!stralloc_copy(&foo, &ufline)) 705 temp_nomem(); 706 if (!stralloc_0(&foo)) 707 temp_nomem(); 708 if (!env_put2("UFLINE", foo.s)) 709 temp_nomem(); 710 711 x = ext; 712 if (!env_put2("EXT", x)) 713 temp_nomem(); 714 x += str_chr(x, '-'); 715 if (*x) 716 ++x; 717 if (!env_put2("EXT2", x)) 718 temp_nomem(); 719 x += str_chr(x, '-'); 720 if (*x) 721 ++x; 722 if (!env_put2("EXT3", x)) 723 temp_nomem(); 724 x += str_chr(x, '-'); 725 if (*x) 726 ++x; 727 if (!env_put2("EXT4", x)) 728 temp_nomem(); 729 730 if (!stralloc_copys(&safeext, ext)) 731 temp_nomem(); 732 case_lowerb(safeext.s, safeext.len); 733 for (i = 0; i < safeext.len; ++i) 734 if (safeext.s[i] == '.') 735 safeext.s[i] = ':'; 736 737 i = str_len(host); 738 i = byte_rchr(host, i, '.'); 739 if (!stralloc_copyb(&foo, host, i)) 740 temp_nomem(); 741 if (!stralloc_0(&foo)) 742 temp_nomem(); 743 if (!env_put2("HOST2", foo.s)) 744 temp_nomem(); 745 i = byte_rchr(host, i, '.'); 746 if (!stralloc_copyb(&foo, host, i)) 747 temp_nomem(); 748 if (!stralloc_0(&foo)) 749 temp_nomem(); 750 if (!env_put2("HOST3", foo.s)) 751 temp_nomem(); 752 i = byte_rchr(host, i, '.'); 753 if (!stralloc_copyb(&foo, host, i)) 754 temp_nomem(); 755 if (!stralloc_0(&foo)) 756 temp_nomem(); 757 if (!env_put2("HOST4", foo.s)) 758 temp_nomem(); 759 760 flagforwardonly = 0; 761 qmesearch(&fd, &flagforwardonly); 762 if (fd == -1) 763 if (*dash) 764 strerr_die1x(100, "5.1.1 Sorry, no mailbox here by that name."); 765 766 if (!stralloc_copys(&ueo, sender)) 767 temp_nomem(); 768 if (str_diff(sender, "")) 769 if (str_diff(sender, "#@[]")) 770 if (qmeox("-owner") == 0) { 771 if (qmeox("-owner-default") == 0) { 772 if (!stralloc_copys(&ueo, local)) 773 temp_nomem(); 774 if (!stralloc_cats(&ueo, "-owner-@")) 775 temp_nomem(); 776 if (!stralloc_cats(&ueo, host)) 777 temp_nomem(); 778 if (!stralloc_cats(&ueo, "-@[]")) 779 temp_nomem(); 780 } else { 781 if (!stralloc_copys(&ueo, local)) 782 temp_nomem(); 783 if (!stralloc_cats(&ueo, "-owner@")) 784 temp_nomem(); 785 if (!stralloc_cats(&ueo, host)) 786 temp_nomem(); 787 } 788 } 789 if (!stralloc_0(&ueo)) 790 temp_nomem(); 791 if (!env_put2("NEWSENDER", ueo.s)) 792 temp_nomem(); 793 794 if (!stralloc_ready(&cmds, 0)) 795 temp_nomem(); 796 cmds.len = 0; 797 /* The full .qmail file must fit into memory for qmail-local. */ 798 if (fd != -1) 799 if (slurpclose(fd, &cmds, 256) == -1) 800 temp_nomem(); 801 802 if (!cmds.len) { 803 if (!stralloc_copys(&cmds, aliasempty)) 804 temp_nomem(); 805 flagforwardonly = 0; 806 } 807 if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) 808 if (!stralloc_cats(&cmds, "\n")) 809 temp_nomem(); 810 811 numforward = 0; 812 i = 0; 813 for (j = 0; j < cmds.len; ++j) 814 if (cmds.s[j] == '\n') { 815 switch (cmds.s[i]) { 816 case '#': 817 case '.': 818 case '/': 819 case '|': 820 break; 821 default: 822 ++numforward; 823 } 824 i = j + 1; 825 } 826 827 recips = calloc(numforward + 1, sizeof(char *)); 828 if (!recips) 829 temp_nomem(); 830 numforward = 0; 831 832 flag99 = 0; 833 834 i = 0; 835 /* Daniel, if you are reading, this is a crock. 836 * An LUT would only cost you 2 to 6 kB on amd64, and is more easily 837 * patched to extend. */ 838 for (j = 0; j < cmds.len; ++j) 839 if (cmds.s[j] == '\n') { 840 unsigned int k = j; 841 cmds.s[j] = 0; 842 while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) 843 cmds.s[--k] = 0; /* I like this, though. Smart. */ 844 switch (cmds.s[i]) { 845 case 0: /* k == i */ 846 if (i) 847 break; 848 strerr_die1x(111, "4.2.1 Uh-oh: first line of .qmail file is blank."); 849 case '#': 850 break; 851 case '.': 852 case '/': 853 ++count_file; 854 if (flagforwardonly) 855 strerr_die1x(111, "4.7.0 Uh-oh: .qmail has file delivery but has x bit set."); 856 if (cmds.s[k - 1] == '/') 857 if (flagdoit) 858 maildir(cmds.s + i); 859 else 860 sayit("maildir ", cmds.s + i, k - i); 861 else if (flagdoit) 862 mailfile(cmds.s + i); 863 else 864 sayit("mboxrd ", cmds.s + i, k - i); 865 break; 866 case '|': 867 ++count_program; 868 if (flagforwardonly) 869 strerr_die1x(111, "4.7.0 Uh-oh: .qmail has prog delivery but has x bit set."); 870 if (flagdoit) 871 mailprogram(cmds.s + i + 1); 872 else 873 sayit("program ", cmds.s + i + 1, k - i - 1); 874 break; 875 case '+': 876 if (str_equal(cmds.s + i + 1, "list")) 877 flagforwardonly = 1; 878 break; 879 case '&': 880 ++i; 881 default: 882 ++count_forward; 883 if (flagdoit) 884 recips[numforward++] = cmds.s + i; 885 else 886 sayit("forward ", cmds.s + i, k - i); 887 break; 888 } 889 i = j + 1; 890 if (flag99) 891 break; 892 } 893 894 if (numforward) 895 if (flagdoit) { 896 recips[numforward] = 0; 897 mailforward(recips); 898 } 899 900 count_print(); 901 _exit(0); 902 }