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