1 /* $OpenBSD: enqueue.c,v 1.82 2014/07/20 01:38:40 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> 5 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 6 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 17 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 18 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/socket.h> 24 #include <sys/tree.h> 25 #include <sys/stat.h> 26 27 #include <ctype.h> 28 #include <err.h> 29 #include <errno.h> 30 #include <event.h> 31 #include <imsg.h> 32 #include <inttypes.h> 33 #include <pwd.h> 34 #include <stdarg.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <sysexits.h> 39 #include <time.h> 40 #include <unistd.h> 41 42 #include "smtpd.h" 43 44 extern struct imsgbuf *ibuf; 45 46 void usage(void); 47 static void build_from(char *, struct passwd *); 48 static int parse_message(FILE *, int, int, FILE *); 49 static void parse_addr(char *, size_t, int); 50 static void parse_addr_terminal(int); 51 static char *qualify_addr(char *); 52 static void rcpt_add(char *); 53 static int open_connection(void); 54 static void get_responses(FILE *, int); 55 static int send_line(FILE *, int, char *, ...); 56 static int enqueue_offline(int, char *[], FILE *); 57 58 extern int srv_connect(void); 59 60 enum headerfields { 61 HDR_NONE, 62 HDR_FROM, 63 HDR_TO, 64 HDR_CC, 65 HDR_BCC, 66 HDR_SUBJECT, 67 HDR_DATE, 68 HDR_MSGID, 69 HDR_MIME_VERSION, 70 HDR_CONTENT_TYPE, 71 HDR_CONTENT_DISPOSITION, 72 HDR_CONTENT_TRANSFER_ENCODING, 73 HDR_USER_AGENT 74 }; 75 76 struct { 77 char *word; 78 enum headerfields type; 79 } keywords[] = { 80 { "From:", HDR_FROM }, 81 { "To:", HDR_TO }, 82 { "Cc:", HDR_CC }, 83 { "Bcc:", HDR_BCC }, 84 { "Subject:", HDR_SUBJECT }, 85 { "Date:", HDR_DATE }, 86 { "Message-Id:", HDR_MSGID }, 87 { "MIME-Version:", HDR_MIME_VERSION }, 88 { "Content-Type:", HDR_CONTENT_TYPE }, 89 { "Content-Disposition:", HDR_CONTENT_DISPOSITION }, 90 { "Content-Transfer-Encoding:", HDR_CONTENT_TRANSFER_ENCODING }, 91 { "User-Agent:", HDR_USER_AGENT }, 92 }; 93 94 #define LINESPLIT 990 95 #define SMTP_LINELEN 1000 96 #define TIMEOUTMSG "Timeout\n" 97 98 #define WSP(c) (c == ' ' || c == '\t') 99 100 int verbose = 0; 101 char host[SMTPD_MAXHOSTNAMELEN]; 102 char *user = NULL; 103 time_t timestamp; 104 105 struct { 106 int fd; 107 char *from; 108 char *fromname; 109 char **rcpts; 110 char *dsn_notify; 111 char *dsn_ret; 112 char *dsn_envid; 113 int rcpt_cnt; 114 int need_linesplit; 115 int saw_date; 116 int saw_msgid; 117 int saw_from; 118 int saw_mime_version; 119 int saw_content_type; 120 int saw_content_disposition; 121 int saw_content_transfer_encoding; 122 int saw_user_agent; 123 } msg; 124 125 struct { 126 uint quote; 127 uint comment; 128 uint esc; 129 uint brackets; 130 size_t wpos; 131 char buf[SMTP_LINELEN]; 132 } pstate; 133 134 static void 135 qp_encoded_write(FILE *fp, char *buf, size_t len) 136 { 137 while (len) { 138 if (*buf == '=') 139 fprintf(fp, "=3D"); 140 else if (*buf == ' ' || *buf == '\t') { 141 char *p = buf; 142 143 while (*p != '\n') { 144 if (*p != ' ' && *p != '\t') 145 break; 146 p++; 147 } 148 if (*p == '\n') 149 fprintf(fp, "=%2X", *buf & 0xff); 150 else 151 fprintf(fp, "%c", *buf & 0xff); 152 } 153 else if (! isprint((unsigned char)*buf) && *buf != '\n') 154 fprintf(fp, "=%2X", *buf & 0xff); 155 else 156 fprintf(fp, "%c", *buf); 157 buf++; 158 len--; 159 } 160 } 161 162 static void 163 send_header(FILE *fout, const char *line, size_t len) 164 { 165 int i; 166 167 if (strncasecmp("To:", line, 3) != 0 && 168 strncasecmp("Cc:", line, 3) != 0 && 169 strncasecmp("Bcc:", line, 4) != 0 && 170 strncasecmp("From:", line, 5) != 0) { 171 send_line(fout, 0, "%.*s", (int)len, line); 172 return; 173 } 174 if (len >= sizeof pstate.buf) { 175 send_line(fout, 0, "%.*s", (int)len, line); 176 return; 177 } 178 179 /* XXX 180 * To, Cc and Bcc may need rewrite, we can reuse the 181 * msg recipients field since former content has already 182 * been used at this point. 183 */ 184 memset(&pstate, 0, sizeof(pstate)); 185 memcpy(pstate.buf, line, len); 186 pstate.buf[len] = 0; 187 pstate.wpos = len - 1; 188 msg.rcpts = NULL; 189 msg.rcpt_cnt = 0; 190 191 if (strncasecmp("From:", line, 5) == 0) { 192 parse_addr_terminal(1); 193 send_line(fout, 0, "%s\n", msg.from); 194 } 195 else { 196 parse_addr_terminal(0); 197 for (i = 0; i < msg.rcpt_cnt; ++i) 198 if (*msg.rcpts[i] != '\0') 199 send_line(fout, 0, "%s%s%s\n", i > 0 ? "\t" : "", 200 msg.rcpts[i], i < msg.rcpt_cnt - 1 ? "," : ""); 201 } 202 } 203 204 int 205 enqueue(int argc, char *argv[]) 206 { 207 int i, ch, tflag = 0, noheader; 208 char *fake_from = NULL, *buf; 209 struct passwd *pw; 210 FILE *fp, *fout; 211 size_t len, envid_sz = 0; 212 int fd; 213 char sfn[] = "/tmp/smtpd.XXXXXXXXXX"; 214 char *line; 215 int dotted; 216 int inheaders = 0; 217 int save_argc; 218 char **save_argv; 219 220 memset(&msg, 0, sizeof(msg)); 221 time(×tamp); 222 223 save_argc = argc; 224 save_argv = argv; 225 226 while ((ch = getopt(argc, argv, 227 "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvV:x")) != -1) { 228 switch (ch) { 229 case 'f': 230 fake_from = optarg; 231 break; 232 case 'F': 233 msg.fromname = optarg; 234 break; 235 case 'N': 236 msg.dsn_notify = optarg; 237 break; 238 case 'R': 239 msg.dsn_ret = optarg; 240 break; 241 case 't': 242 tflag = 1; 243 break; 244 case 'v': 245 verbose = 1; 246 break; 247 case 'V': 248 msg.dsn_envid = optarg; 249 break; 250 /* all remaining: ignored, sendmail compat */ 251 case 'A': 252 case 'B': 253 case 'b': 254 case 'E': 255 case 'e': 256 case 'i': 257 case 'L': 258 case 'm': 259 case 'o': 260 case 'p': 261 case 'x': 262 break; 263 case 'q': 264 /* XXX: implement "process all now" */ 265 return (EX_SOFTWARE); 266 default: 267 usage(); 268 } 269 } 270 271 argc -= optind; 272 argv += optind; 273 274 if (getmailname(host, sizeof(host)) == -1) 275 err(EX_NOHOST, "getmailname"); 276 if ((pw = getpwuid(getuid())) == NULL) 277 user = "anonymous"; 278 if (pw != NULL) 279 user = xstrdup(pw->pw_name, "enqueue"); 280 281 build_from(fake_from, pw); 282 283 while (argc > 0) { 284 rcpt_add(argv[0]); 285 argv++; 286 argc--; 287 } 288 289 if ((fd = mkstemp(sfn)) == -1 || 290 (fp = fdopen(fd, "w+")) == NULL) { 291 int saved_errno = errno; 292 if (fd != -1) { 293 unlink(sfn); 294 close(fd); 295 } 296 errc(EX_UNAVAILABLE, saved_errno, "mkstemp"); 297 } 298 unlink(sfn); 299 noheader = parse_message(stdin, fake_from == NULL, tflag, fp); 300 301 if (msg.rcpt_cnt == 0) 302 errx(EX_SOFTWARE, "no recipients"); 303 304 /* init session */ 305 rewind(fp); 306 307 /* try to connect */ 308 /* If the server is not running, enqueue the message offline */ 309 310 if (!srv_connect()) 311 return (enqueue_offline(save_argc, save_argv, fp)); 312 313 if ((msg.fd = open_connection()) == -1) 314 errx(EX_UNAVAILABLE, "server too busy"); 315 316 fout = fdopen(msg.fd, "a+"); 317 if (fout == NULL) 318 err(EX_UNAVAILABLE, "fdopen"); 319 320 /* 321 * We need to call get_responses after every command because we don't 322 * support PIPELINING on the server-side yet. 323 */ 324 325 /* banner */ 326 get_responses(fout, 1); 327 328 send_line(fout, verbose, "EHLO localhost\n"); 329 get_responses(fout, 1); 330 331 if (msg.dsn_envid != NULL) 332 envid_sz = strlen(msg.dsn_envid); 333 334 send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n", 335 msg.from, 336 msg.dsn_ret ? "RET=" : "", 337 msg.dsn_ret ? msg.dsn_ret : "", 338 envid_sz ? "ENVID=" : "", 339 envid_sz ? msg.dsn_envid : ""); 340 get_responses(fout, 1); 341 342 for (i = 0; i < msg.rcpt_cnt; i++) { 343 send_line(fout, verbose, "RCPT TO:<%s> %s%s\n", 344 msg.rcpts[i], 345 msg.dsn_notify ? "NOTIFY=" : "", 346 msg.dsn_notify ? msg.dsn_notify : ""); 347 get_responses(fout, 1); 348 } 349 350 send_line(fout, verbose, "DATA\n"); 351 get_responses(fout, 1); 352 353 /* add From */ 354 if (!msg.saw_from) 355 send_line(fout, 0, "From: %s%s<%s>\n", 356 msg.fromname ? msg.fromname : "", 357 msg.fromname ? " " : "", 358 msg.from); 359 360 /* add Date */ 361 if (!msg.saw_date) 362 send_line(fout, 0, "Date: %s\n", time_to_text(timestamp)); 363 364 /* add Message-Id */ 365 if (!msg.saw_msgid) 366 send_line(fout, 0, "Message-Id: <%"PRIu64".enqueue@%s>\n", 367 generate_uid(), host); 368 369 if (msg.need_linesplit) { 370 /* we will always need to mime encode for long lines */ 371 if (!msg.saw_mime_version) 372 send_line(fout, 0, "MIME-Version: 1.0\n"); 373 if (!msg.saw_content_type) 374 send_line(fout, 0, "Content-Type: text/plain; " 375 "charset=unknown-8bit\n"); 376 if (!msg.saw_content_disposition) 377 send_line(fout, 0, "Content-Disposition: inline\n"); 378 if (!msg.saw_content_transfer_encoding) 379 send_line(fout, 0, "Content-Transfer-Encoding: " 380 "quoted-printable\n"); 381 } 382 383 /* add separating newline */ 384 if (noheader) 385 send_line(fout, 0, "\n"); 386 else 387 inheaders = 1; 388 389 for (;;) { 390 buf = fgetln(fp, &len); 391 if (buf == NULL && ferror(fp)) 392 err(EX_UNAVAILABLE, "fgetln"); 393 if (buf == NULL && feof(fp)) 394 break; 395 /* newlines have been normalized on first parsing */ 396 if (buf[len-1] != '\n') 397 errx(EX_SOFTWARE, "expect EOL"); 398 399 dotted = 0; 400 if (buf[0] == '.') { 401 fputc('.', fout); 402 dotted = 1; 403 } 404 405 line = buf; 406 407 if (msg.saw_content_transfer_encoding || noheader || 408 inheaders || !msg.need_linesplit) { 409 if (inheaders) 410 send_header(fout, line, len); 411 else 412 send_line(fout, 0, "%.*s", (int)len, line); 413 if (inheaders && buf[0] == '\n') 414 inheaders = 0; 415 continue; 416 } 417 418 /* we don't have a content transfer encoding, use our default */ 419 do { 420 if (len < LINESPLIT) { 421 qp_encoded_write(fout, line, len); 422 break; 423 } 424 else { 425 qp_encoded_write(fout, line, 426 LINESPLIT - 2 - dotted); 427 send_line(fout, 0, "=\n"); 428 line += LINESPLIT - 2 - dotted; 429 len -= LINESPLIT - 2 - dotted; 430 } 431 } while (len); 432 } 433 send_line(fout, verbose, ".\n"); 434 get_responses(fout, 1); 435 436 send_line(fout, verbose, "QUIT\n"); 437 get_responses(fout, 1); 438 439 fclose(fp); 440 fclose(fout); 441 442 exit(EX_OK); 443 } 444 445 static void 446 get_responses(FILE *fin, int n) 447 { 448 char *buf; 449 size_t len; 450 int e; 451 452 fflush(fin); 453 if ((e = ferror(fin))) 454 errx(1, "ferror: %d", e); 455 456 while (n) { 457 buf = fgetln(fin, &len); 458 if (buf == NULL && ferror(fin)) 459 err(1, "fgetln"); 460 if (buf == NULL && feof(fin)) 461 break; 462 if (buf == NULL || len < 1) 463 err(1, "fgetln weird"); 464 465 /* account for \r\n linebreaks */ 466 if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') 467 buf[--len - 1] = '\n'; 468 469 if (len < 4) 470 errx(1, "bad response"); 471 472 if (verbose) 473 printf("<<< %.*s", (int)len, buf); 474 475 if (buf[3] == '-') 476 continue; 477 if (buf[0] != '2' && buf[0] != '3') 478 errx(1, "command failed: %.*s", (int)len, buf); 479 n--; 480 } 481 } 482 483 static int 484 send_line(FILE *fp, int v, char *fmt, ...) 485 { 486 int ret; 487 va_list ap; 488 489 va_start(ap, fmt); 490 ret = vfprintf(fp, fmt, ap); 491 va_end(ap); 492 493 if (v) { 494 va_start(ap, fmt); 495 printf(">>> "); 496 ret = vprintf(fmt, ap); 497 va_end(ap); 498 } 499 500 return (ret); 501 } 502 503 static void 504 build_from(char *fake_from, struct passwd *pw) 505 { 506 char *p; 507 508 if (fake_from == NULL) 509 msg.from = qualify_addr(user); 510 else { 511 if (fake_from[0] == '<') { 512 if (fake_from[strlen(fake_from) - 1] != '>') 513 errx(1, "leading < but no trailing >"); 514 fake_from[strlen(fake_from) - 1] = 0; 515 p = xstrdup(fake_from + 1, "build_from"); 516 517 msg.from = qualify_addr(p); 518 free(p); 519 } else 520 msg.from = qualify_addr(fake_from); 521 } 522 523 if (msg.fromname == NULL && fake_from == NULL && pw != NULL) { 524 int len, apos; 525 526 len = strcspn(pw->pw_gecos, ","); 527 if ((p = memchr(pw->pw_gecos, '&', len))) { 528 apos = p - pw->pw_gecos; 529 if (asprintf(&msg.fromname, "%.*s%s%.*s", 530 apos, pw->pw_gecos, 531 pw->pw_name, 532 len - apos - 1, p + 1) == -1) 533 err(1, NULL); 534 msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]); 535 } else { 536 if (asprintf(&msg.fromname, "%.*s", len, 537 pw->pw_gecos) == -1) 538 err(1, NULL); 539 } 540 } 541 } 542 543 static int 544 parse_message(FILE *fin, int get_from, int tflag, FILE *fout) 545 { 546 char *buf; 547 size_t len; 548 uint i, cur = HDR_NONE; 549 uint header_seen = 0, header_done = 0; 550 551 memset(&pstate, 0, sizeof(pstate)); 552 for (;;) { 553 buf = fgetln(fin, &len); 554 if (buf == NULL && ferror(fin)) 555 err(1, "fgetln"); 556 if (buf == NULL && feof(fin)) 557 break; 558 if (buf == NULL || len < 1) 559 err(1, "fgetln weird"); 560 561 /* account for \r\n linebreaks */ 562 if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n') 563 buf[--len - 1] = '\n'; 564 565 if (len == 1 && buf[0] == '\n') /* end of header */ 566 header_done = 1; 567 568 if (!WSP(buf[0])) { /* whitespace -> continuation */ 569 if (cur == HDR_FROM) 570 parse_addr_terminal(1); 571 if (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC) 572 parse_addr_terminal(0); 573 cur = HDR_NONE; 574 } 575 576 /* not really exact, if we are still in headers */ 577 if (len + (buf[len - 1] == '\n' ? 0 : 1) >= LINESPLIT) 578 msg.need_linesplit = 1; 579 580 for (i = 0; !header_done && cur == HDR_NONE && 581 i < nitems(keywords); i++) 582 if (len > strlen(keywords[i].word) && 583 !strncasecmp(buf, keywords[i].word, 584 strlen(keywords[i].word))) 585 cur = keywords[i].type; 586 587 if (cur != HDR_NONE) 588 header_seen = 1; 589 590 if (cur != HDR_BCC) { 591 send_line(fout, 0, "%.*s", (int)len, buf); 592 if (buf[len - 1] != '\n') 593 fputc('\n', fout); 594 if (ferror(fout)) 595 err(1, "write error"); 596 } 597 598 /* 599 * using From: as envelope sender is not sendmail compatible, 600 * but I really want it that way - maybe needs a knob 601 */ 602 if (cur == HDR_FROM) { 603 msg.saw_from++; 604 if (get_from) 605 parse_addr(buf, len, 1); 606 } 607 608 if (tflag && (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC)) 609 parse_addr(buf, len, 0); 610 611 if (cur == HDR_DATE) 612 msg.saw_date++; 613 if (cur == HDR_MSGID) 614 msg.saw_msgid++; 615 if (cur == HDR_MIME_VERSION) 616 msg.saw_mime_version = 1; 617 if (cur == HDR_CONTENT_TYPE) 618 msg.saw_content_type = 1; 619 if (cur == HDR_CONTENT_DISPOSITION) 620 msg.saw_content_disposition = 1; 621 if (cur == HDR_CONTENT_TRANSFER_ENCODING) 622 msg.saw_content_transfer_encoding = 1; 623 if (cur == HDR_USER_AGENT) 624 msg.saw_user_agent = 1; 625 } 626 627 return (!header_seen); 628 } 629 630 static void 631 parse_addr(char *s, size_t len, int is_from) 632 { 633 size_t pos = 0; 634 int terminal = 0; 635 636 /* unless this is a continuation... */ 637 if (!WSP(s[pos]) && s[pos] != ',' && s[pos] != ';') { 638 /* ... skip over everything before the ':' */ 639 for (; pos < len && s[pos] != ':'; pos++) 640 ; /* nothing */ 641 /* ... and check & reset parser state */ 642 parse_addr_terminal(is_from); 643 } 644 645 /* skip over ':' ',' ';' and whitespace */ 646 for (; pos < len && !pstate.quote && (WSP(s[pos]) || s[pos] == ':' || 647 s[pos] == ',' || s[pos] == ';'); pos++) 648 ; /* nothing */ 649 650 for (; pos < len; pos++) { 651 if (!pstate.esc && !pstate.quote && s[pos] == '(') 652 pstate.comment++; 653 if (!pstate.comment && !pstate.esc && s[pos] == '"') 654 pstate.quote = !pstate.quote; 655 656 if (!pstate.comment && !pstate.quote && !pstate.esc) { 657 if (s[pos] == ':') { /* group */ 658 for (pos++; pos < len && WSP(s[pos]); pos++) 659 ; /* nothing */ 660 pstate.wpos = 0; 661 } 662 if (s[pos] == '\n' || s[pos] == '\r') 663 break; 664 if (s[pos] == ',' || s[pos] == ';') { 665 terminal = 1; 666 break; 667 } 668 if (s[pos] == '<') { 669 pstate.brackets = 1; 670 pstate.wpos = 0; 671 } 672 if (pstate.brackets && s[pos] == '>') 673 terminal = 1; 674 } 675 676 if (!pstate.comment && !terminal && (!(!(pstate.quote || 677 pstate.esc) && (s[pos] == '<' || WSP(s[pos]))))) { 678 if (pstate.wpos >= sizeof(pstate.buf)) 679 errx(1, "address exceeds buffer size"); 680 pstate.buf[pstate.wpos++] = s[pos]; 681 } 682 683 if (!pstate.quote && pstate.comment && s[pos] == ')') 684 pstate.comment--; 685 686 if (!pstate.esc && !pstate.comment && !pstate.quote && 687 s[pos] == '\\') 688 pstate.esc = 1; 689 else 690 pstate.esc = 0; 691 } 692 693 if (terminal) 694 parse_addr_terminal(is_from); 695 696 for (; pos < len && (s[pos] == '\r' || s[pos] == '\n'); pos++) 697 ; /* nothing */ 698 699 if (pos < len) 700 parse_addr(s + pos, len - pos, is_from); 701 } 702 703 static void 704 parse_addr_terminal(int is_from) 705 { 706 if (pstate.comment || pstate.quote || pstate.esc) 707 errx(1, "syntax error in address"); 708 if (pstate.wpos) { 709 if (pstate.wpos >= sizeof(pstate.buf)) 710 errx(1, "address exceeds buffer size"); 711 pstate.buf[pstate.wpos] = '\0'; 712 if (is_from) 713 msg.from = qualify_addr(pstate.buf); 714 else 715 rcpt_add(pstate.buf); 716 pstate.wpos = 0; 717 } 718 } 719 720 static char * 721 qualify_addr(char *in) 722 { 723 char *out; 724 725 if (strlen(in) > 0 && strchr(in, '@') == NULL) { 726 if (asprintf(&out, "%s@%s", in, host) == -1) 727 err(1, "qualify asprintf"); 728 } else 729 out = xstrdup(in, "qualify_addr"); 730 731 return (out); 732 } 733 734 static void 735 rcpt_add(char *addr) 736 { 737 void *nrcpts; 738 char *p; 739 int n; 740 741 n = 1; 742 p = addr; 743 while ((p = strchr(p, ',')) != NULL) { 744 n++; 745 p++; 746 } 747 748 if ((nrcpts = realloc(msg.rcpts, 749 sizeof(char *) * (msg.rcpt_cnt + n))) == NULL) 750 err(1, "rcpt_add realloc"); 751 msg.rcpts = nrcpts; 752 753 while (n--) { 754 if ((p = strchr(addr, ',')) != NULL) 755 *p++ = '\0'; 756 msg.rcpts[msg.rcpt_cnt++] = qualify_addr(addr); 757 if (p == NULL) 758 break; 759 addr = p; 760 } 761 } 762 763 static int 764 open_connection(void) 765 { 766 struct imsg imsg; 767 int fd; 768 int n; 769 770 imsg_compose(ibuf, IMSG_CTL_SMTP_SESSION, IMSG_VERSION, 0, -1, NULL, 0); 771 772 while (ibuf->w.queued) 773 if (msgbuf_write(&ibuf->w) < 0 && errno != EAGAIN) 774 err(1, "write error"); 775 776 while (1) { 777 if ((n = imsg_read(ibuf)) == -1) 778 errx(1, "imsg_read error"); 779 if (n == 0) 780 errx(1, "pipe closed"); 781 782 if ((n = imsg_get(ibuf, &imsg)) == -1) 783 errx(1, "imsg_get error"); 784 if (n == 0) 785 continue; 786 787 switch (imsg.hdr.type) { 788 case IMSG_CTL_OK: 789 break; 790 case IMSG_CTL_FAIL: 791 errx(1, "server disallowed submission request"); 792 default: 793 errx(1, "unexpected imsg reply type"); 794 } 795 796 fd = imsg.fd; 797 imsg_free(&imsg); 798 799 break; 800 } 801 802 return fd; 803 } 804 805 static int 806 enqueue_offline(int argc, char *argv[], FILE *ifile) 807 { 808 char path[SMTPD_MAXPATHLEN]; 809 FILE *fp; 810 int i, fd, ch; 811 mode_t omode; 812 813 if (ckdir(PATH_SPOOL PATH_OFFLINE, 01777, 0, 0, 0) == 0) 814 errx(EX_UNAVAILABLE, "error in offline directory setup"); 815 816 if (! bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL, 817 PATH_OFFLINE, (long long int) time(NULL))) 818 err(EX_UNAVAILABLE, "snprintf"); 819 820 omode = umask(7077); 821 if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { 822 warn("cannot create temporary file %s", path); 823 if (fd != -1) 824 unlink(path); 825 exit(EX_UNAVAILABLE); 826 } 827 umask(omode); 828 829 if (fchmod(fd, 0600) == -1) { 830 unlink(path); 831 exit(EX_SOFTWARE); 832 } 833 834 for (i = 1; i < argc; i++) { 835 if (strchr(argv[i], '|') != NULL) { 836 warnx("%s contains illegal character", argv[i]); 837 unlink(path); 838 exit(EX_SOFTWARE); 839 } 840 fprintf(fp, "%s%s", i == 1 ? "" : "|", argv[i]); 841 } 842 843 fprintf(fp, "\n"); 844 845 while ((ch = fgetc(ifile)) != EOF) 846 if (fputc(ch, fp) == EOF) { 847 warn("write error"); 848 unlink(path); 849 exit(EX_UNAVAILABLE); 850 } 851 852 if (ferror(ifile)) { 853 warn("read error"); 854 unlink(path); 855 exit(EX_UNAVAILABLE); 856 } 857 858 fclose(fp); 859 860 return (EX_TEMPFAIL); 861 } 862