xref: /openbsd-src/usr.sbin/smtpd/enqueue.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: enqueue.c,v 1.81 2014/06/06 15:02:08 gilles 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(&timestamp);
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 		if (fd != -1) {
292 			unlink(sfn);
293 			close(fd);
294 		}
295 		err(EX_UNAVAILABLE, "mkstemp");
296 	}
297 	unlink(sfn);
298 	noheader = parse_message(stdin, fake_from == NULL, tflag, fp);
299 
300 	if (msg.rcpt_cnt == 0)
301 		errx(EX_SOFTWARE, "no recipients");
302 
303 	/* init session */
304 	rewind(fp);
305 
306 	/* try to connect */
307 	/* If the server is not running, enqueue the message offline */
308 
309 	if (!srv_connect())
310 		return (enqueue_offline(save_argc, save_argv, fp));
311 
312 	if ((msg.fd = open_connection()) == -1)
313 		errx(EX_UNAVAILABLE, "server too busy");
314 
315 	fout = fdopen(msg.fd, "a+");
316 	if (fout == NULL)
317 		err(EX_UNAVAILABLE, "fdopen");
318 
319 	/*
320 	 * We need to call get_responses after every command because we don't
321 	 * support PIPELINING on the server-side yet.
322 	 */
323 
324 	/* banner */
325 	get_responses(fout, 1);
326 
327 	send_line(fout, verbose, "EHLO localhost\n");
328 	get_responses(fout, 1);
329 
330 	if (msg.dsn_envid != NULL)
331 		envid_sz = strlen(msg.dsn_envid);
332 
333 	send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n",
334 	    msg.from,
335 	    msg.dsn_ret ? "RET=" : "",
336 	    msg.dsn_ret ? msg.dsn_ret : "",
337 	    envid_sz ? "ENVID=" : "",
338 	    envid_sz ? msg.dsn_envid : "");
339 	get_responses(fout, 1);
340 
341 	for (i = 0; i < msg.rcpt_cnt; i++) {
342 		send_line(fout, verbose, "RCPT TO:<%s> %s%s\n",
343 		    msg.rcpts[i],
344 		    msg.dsn_notify ? "NOTIFY=" : "",
345 		    msg.dsn_notify ? msg.dsn_notify : "");
346 		get_responses(fout, 1);
347 	}
348 
349 	send_line(fout, verbose, "DATA\n");
350 	get_responses(fout, 1);
351 
352 	/* add From */
353 	if (!msg.saw_from)
354 		send_line(fout, 0, "From: %s%s<%s>\n",
355 		    msg.fromname ? msg.fromname : "",
356 		    msg.fromname ? " " : "",
357 		    msg.from);
358 
359 	/* add Date */
360 	if (!msg.saw_date)
361 		send_line(fout, 0, "Date: %s\n", time_to_text(timestamp));
362 
363 	/* add Message-Id */
364 	if (!msg.saw_msgid)
365 		send_line(fout, 0, "Message-Id: <%"PRIu64".enqueue@%s>\n",
366 		    generate_uid(), host);
367 
368 	if (msg.need_linesplit) {
369 		/* we will always need to mime encode for long lines */
370 		if (!msg.saw_mime_version)
371 			send_line(fout, 0, "MIME-Version: 1.0\n");
372 		if (!msg.saw_content_type)
373 			send_line(fout, 0, "Content-Type: text/plain; "
374 			    "charset=unknown-8bit\n");
375 		if (!msg.saw_content_disposition)
376 			send_line(fout, 0, "Content-Disposition: inline\n");
377 		if (!msg.saw_content_transfer_encoding)
378 			send_line(fout, 0, "Content-Transfer-Encoding: "
379 			    "quoted-printable\n");
380 	}
381 
382 	/* add separating newline */
383 	if (noheader)
384 		send_line(fout, 0, "\n");
385 	else
386 		inheaders = 1;
387 
388 	for (;;) {
389 		buf = fgetln(fp, &len);
390 		if (buf == NULL && ferror(fp))
391 			err(EX_UNAVAILABLE, "fgetln");
392 		if (buf == NULL && feof(fp))
393 			break;
394 		/* newlines have been normalized on first parsing */
395 		if (buf[len-1] != '\n')
396 			errx(EX_SOFTWARE, "expect EOL");
397 
398 		dotted = 0;
399 		if (buf[0] == '.') {
400 			fputc('.', fout);
401 			dotted = 1;
402 		}
403 
404 		line = buf;
405 
406 		if (msg.saw_content_transfer_encoding || noheader ||
407 		    inheaders || !msg.need_linesplit) {
408 			if (inheaders)
409 				send_header(fout, line, len);
410 			else
411 				send_line(fout, 0, "%.*s", (int)len, line);
412 			if (inheaders && buf[0] == '\n')
413 				inheaders = 0;
414 			continue;
415 		}
416 
417 		/* we don't have a content transfer encoding, use our default */
418 		do {
419 			if (len < LINESPLIT) {
420 				qp_encoded_write(fout, line, len);
421 				break;
422 			}
423 			else {
424 				qp_encoded_write(fout, line,
425 				    LINESPLIT - 2 - dotted);
426 				send_line(fout, 0, "=\n");
427 				line += LINESPLIT - 2 - dotted;
428 				len -= LINESPLIT - 2 - dotted;
429 			}
430 		} while (len);
431 	}
432 	send_line(fout, verbose, ".\n");
433 	get_responses(fout, 1);
434 
435 	send_line(fout, verbose, "QUIT\n");
436 	get_responses(fout, 1);
437 
438 	fclose(fp);
439 	fclose(fout);
440 
441 	exit(EX_OK);
442 }
443 
444 static void
445 get_responses(FILE *fin, int n)
446 {
447 	char	*buf;
448 	size_t	 len;
449 	int	 e;
450 
451 	fflush(fin);
452 	if ((e = ferror(fin)))
453 		errx(1, "ferror: %d", e);
454 
455 	while (n) {
456 		buf = fgetln(fin, &len);
457 		if (buf == NULL && ferror(fin))
458 			err(1, "fgetln");
459 		if (buf == NULL && feof(fin))
460 			break;
461 		if (buf == NULL || len < 1)
462 			err(1, "fgetln weird");
463 
464 		/* account for \r\n linebreaks */
465 		if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n')
466 			buf[--len - 1] = '\n';
467 
468 		if (len < 4)
469 			errx(1, "bad response");
470 
471 		if (verbose)
472 			printf("<<< %.*s", (int)len, buf);
473 
474 		if (buf[3] == '-')
475 			continue;
476 		if (buf[0] != '2' && buf[0] != '3')
477 			errx(1, "command failed: %.*s", (int)len, buf);
478 		n--;
479 	}
480 }
481 
482 static int
483 send_line(FILE *fp, int v, char *fmt, ...)
484 {
485 	int ret;
486 	va_list ap;
487 
488 	va_start(ap, fmt);
489 	ret = vfprintf(fp, fmt, ap);
490 	va_end(ap);
491 
492 	if (v) {
493 		va_start(ap, fmt);
494 		printf(">>> ");
495 		ret = vprintf(fmt, ap);
496 		va_end(ap);
497 	}
498 
499 	return (ret);
500 }
501 
502 static void
503 build_from(char *fake_from, struct passwd *pw)
504 {
505 	char	*p;
506 
507 	if (fake_from == NULL)
508 		msg.from = qualify_addr(user);
509 	else {
510 		if (fake_from[0] == '<') {
511 			if (fake_from[strlen(fake_from) - 1] != '>')
512 				errx(1, "leading < but no trailing >");
513 			fake_from[strlen(fake_from) - 1] = 0;
514 			p = xstrdup(fake_from + 1, "build_from");
515 
516 			msg.from = qualify_addr(p);
517 			free(p);
518 		} else
519 			msg.from = qualify_addr(fake_from);
520 	}
521 
522 	if (msg.fromname == NULL && fake_from == NULL && pw != NULL) {
523 		int	 len, apos;
524 
525 		len = strcspn(pw->pw_gecos, ",");
526 		if ((p = memchr(pw->pw_gecos, '&', len))) {
527 			apos = p - pw->pw_gecos;
528 			if (asprintf(&msg.fromname, "%.*s%s%.*s",
529 			    apos, pw->pw_gecos,
530 			    pw->pw_name,
531 			    len - apos - 1, p + 1) == -1)
532 				err(1, NULL);
533 			msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]);
534 		} else {
535 			if (asprintf(&msg.fromname, "%.*s", len,
536 			    pw->pw_gecos) == -1)
537 				err(1, NULL);
538 		}
539 	}
540 }
541 
542 static int
543 parse_message(FILE *fin, int get_from, int tflag, FILE *fout)
544 {
545 	char	*buf;
546 	size_t	 len;
547 	uint	 i, cur = HDR_NONE;
548 	uint	 header_seen = 0, header_done = 0;
549 
550 	memset(&pstate, 0, sizeof(pstate));
551 	for (;;) {
552 		buf = fgetln(fin, &len);
553 		if (buf == NULL && ferror(fin))
554 			err(1, "fgetln");
555 		if (buf == NULL && feof(fin))
556 			break;
557 		if (buf == NULL || len < 1)
558 			err(1, "fgetln weird");
559 
560 		/* account for \r\n linebreaks */
561 		if (len >= 2 && buf[len - 2] == '\r' && buf[len - 1] == '\n')
562 			buf[--len - 1] = '\n';
563 
564 		if (len == 1 && buf[0] == '\n')		/* end of header */
565 			header_done = 1;
566 
567 		if (!WSP(buf[0])) {	/* whitespace -> continuation */
568 			if (cur == HDR_FROM)
569 				parse_addr_terminal(1);
570 			if (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC)
571 				parse_addr_terminal(0);
572 			cur = HDR_NONE;
573 		}
574 
575 		/* not really exact, if we are still in headers */
576 		if (len + (buf[len - 1] == '\n' ? 0 : 1) >= LINESPLIT)
577 			msg.need_linesplit = 1;
578 
579 		for (i = 0; !header_done && cur == HDR_NONE &&
580 		    i < nitems(keywords); i++)
581 			if (len > strlen(keywords[i].word) &&
582 			    !strncasecmp(buf, keywords[i].word,
583 			    strlen(keywords[i].word)))
584 				cur = keywords[i].type;
585 
586 		if (cur != HDR_NONE)
587 			header_seen = 1;
588 
589 		if (cur != HDR_BCC) {
590 			send_line(fout, 0, "%.*s", (int)len, buf);
591 			if (buf[len - 1] != '\n')
592 				fputc('\n', fout);
593 			if (ferror(fout))
594 				err(1, "write error");
595 		}
596 
597 		/*
598 		 * using From: as envelope sender is not sendmail compatible,
599 		 * but I really want it that way - maybe needs a knob
600 		 */
601 		if (cur == HDR_FROM) {
602 			msg.saw_from++;
603 			if (get_from)
604 				parse_addr(buf, len, 1);
605 		}
606 
607 		if (tflag && (cur == HDR_TO || cur == HDR_CC || cur == HDR_BCC))
608 			parse_addr(buf, len, 0);
609 
610 		if (cur == HDR_DATE)
611 			msg.saw_date++;
612 		if (cur == HDR_MSGID)
613 			msg.saw_msgid++;
614 		if (cur == HDR_MIME_VERSION)
615 			msg.saw_mime_version = 1;
616 		if (cur == HDR_CONTENT_TYPE)
617 			msg.saw_content_type = 1;
618 		if (cur == HDR_CONTENT_DISPOSITION)
619 			msg.saw_content_disposition = 1;
620 		if (cur == HDR_CONTENT_TRANSFER_ENCODING)
621 			msg.saw_content_transfer_encoding = 1;
622 		if (cur == HDR_USER_AGENT)
623 			msg.saw_user_agent = 1;
624 	}
625 
626 	return (!header_seen);
627 }
628 
629 static void
630 parse_addr(char *s, size_t len, int is_from)
631 {
632 	size_t	 pos = 0;
633 	int	 terminal = 0;
634 
635 	/* unless this is a continuation... */
636 	if (!WSP(s[pos]) && s[pos] != ',' && s[pos] != ';') {
637 		/* ... skip over everything before the ':' */
638 		for (; pos < len && s[pos] != ':'; pos++)
639 			;	/* nothing */
640 		/* ... and check & reset parser state */
641 		parse_addr_terminal(is_from);
642 	}
643 
644 	/* skip over ':' ',' ';' and whitespace */
645 	for (; pos < len && !pstate.quote && (WSP(s[pos]) || s[pos] == ':' ||
646 	    s[pos] == ',' || s[pos] == ';'); pos++)
647 		;	/* nothing */
648 
649 	for (; pos < len; pos++) {
650 		if (!pstate.esc && !pstate.quote && s[pos] == '(')
651 			pstate.comment++;
652 		if (!pstate.comment && !pstate.esc && s[pos] == '"')
653 			pstate.quote = !pstate.quote;
654 
655 		if (!pstate.comment && !pstate.quote && !pstate.esc) {
656 			if (s[pos] == ':') {	/* group */
657 				for (pos++; pos < len && WSP(s[pos]); pos++)
658 					;	/* nothing */
659 				pstate.wpos = 0;
660 			}
661 			if (s[pos] == '\n' || s[pos] == '\r')
662 				break;
663 			if (s[pos] == ',' || s[pos] == ';') {
664 				terminal = 1;
665 				break;
666 			}
667 			if (s[pos] == '<') {
668 				pstate.brackets = 1;
669 				pstate.wpos = 0;
670 			}
671 			if (pstate.brackets && s[pos] == '>')
672 				terminal = 1;
673 		}
674 
675 		if (!pstate.comment && !terminal && (!(!(pstate.quote ||
676 		    pstate.esc) && (s[pos] == '<' || WSP(s[pos]))))) {
677 			if (pstate.wpos >= sizeof(pstate.buf))
678 				errx(1, "address exceeds buffer size");
679 			pstate.buf[pstate.wpos++] = s[pos];
680 		}
681 
682 		if (!pstate.quote && pstate.comment && s[pos] == ')')
683 			pstate.comment--;
684 
685 		if (!pstate.esc && !pstate.comment && !pstate.quote &&
686 		    s[pos] == '\\')
687 			pstate.esc = 1;
688 		else
689 			pstate.esc = 0;
690 	}
691 
692 	if (terminal)
693 		parse_addr_terminal(is_from);
694 
695 	for (; pos < len && (s[pos] == '\r' || s[pos] == '\n'); pos++)
696 		;	/* nothing */
697 
698 	if (pos < len)
699 		parse_addr(s + pos, len - pos, is_from);
700 }
701 
702 static void
703 parse_addr_terminal(int is_from)
704 {
705 	if (pstate.comment || pstate.quote || pstate.esc)
706 		errx(1, "syntax error in address");
707 	if (pstate.wpos) {
708 		if (pstate.wpos >= sizeof(pstate.buf))
709 			errx(1, "address exceeds buffer size");
710 		pstate.buf[pstate.wpos] = '\0';
711 		if (is_from)
712 			msg.from = qualify_addr(pstate.buf);
713 		else
714 			rcpt_add(pstate.buf);
715 		pstate.wpos = 0;
716 	}
717 }
718 
719 static char *
720 qualify_addr(char *in)
721 {
722 	char	*out;
723 
724 	if (strlen(in) > 0 && strchr(in, '@') == NULL) {
725 		if (asprintf(&out, "%s@%s", in, host) == -1)
726 			err(1, "qualify asprintf");
727 	} else
728 		out = xstrdup(in, "qualify_addr");
729 
730 	return (out);
731 }
732 
733 static void
734 rcpt_add(char *addr)
735 {
736 	void	*nrcpts;
737 	char	*p;
738 	int	n;
739 
740 	n = 1;
741 	p = addr;
742 	while ((p = strchr(p, ',')) != NULL) {
743 		n++;
744 		p++;
745 	}
746 
747 	if ((nrcpts = realloc(msg.rcpts,
748 	    sizeof(char *) * (msg.rcpt_cnt + n))) == NULL)
749 		err(1, "rcpt_add realloc");
750 	msg.rcpts = nrcpts;
751 
752 	while (n--) {
753 		if ((p = strchr(addr, ',')) != NULL)
754 			*p++ = '\0';
755 		msg.rcpts[msg.rcpt_cnt++] = qualify_addr(addr);
756 		if (p == NULL)
757 			break;
758 		addr = p;
759 	}
760 }
761 
762 static int
763 open_connection(void)
764 {
765 	struct imsg	imsg;
766 	int		fd;
767 	int		n;
768 
769 	imsg_compose(ibuf, IMSG_CTL_SMTP_SESSION, IMSG_VERSION, 0, -1, NULL, 0);
770 
771 	while (ibuf->w.queued)
772 		if (msgbuf_write(&ibuf->w) < 0 && errno != EAGAIN)
773 			err(1, "write error");
774 
775 	while (1) {
776 		if ((n = imsg_read(ibuf)) == -1)
777 			errx(1, "imsg_read error");
778 		if (n == 0)
779 			errx(1, "pipe closed");
780 
781 		if ((n = imsg_get(ibuf, &imsg)) == -1)
782 			errx(1, "imsg_get error");
783 		if (n == 0)
784 			continue;
785 
786 		switch (imsg.hdr.type) {
787 		case IMSG_CTL_OK:
788 			break;
789 		case IMSG_CTL_FAIL:
790 			errx(1, "server disallowed submission request");
791 		default:
792 			errx(1, "unexpected imsg reply type");
793 		}
794 
795 		fd = imsg.fd;
796 		imsg_free(&imsg);
797 
798 		break;
799 	}
800 
801 	return fd;
802 }
803 
804 static int
805 enqueue_offline(int argc, char *argv[], FILE *ifile)
806 {
807 	char	 path[SMTPD_MAXPATHLEN];
808 	FILE	*fp;
809 	int	 i, fd, ch;
810 	mode_t	 omode;
811 
812 	if (ckdir(PATH_SPOOL PATH_OFFLINE, 01777, 0, 0, 0) == 0)
813 		errx(EX_UNAVAILABLE, "error in offline directory setup");
814 
815 	if (! bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL,
816 		PATH_OFFLINE, (long long int) time(NULL)))
817 		err(EX_UNAVAILABLE, "snprintf");
818 
819 	omode = umask(7077);
820 	if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
821 		warn("cannot create temporary file %s", path);
822 		if (fd != -1)
823 			unlink(path);
824 		exit(EX_UNAVAILABLE);
825 	}
826 	umask(omode);
827 
828 	if (fchmod(fd, 0600) == -1) {
829 		unlink(path);
830 		exit(EX_SOFTWARE);
831 	}
832 
833 	for (i = 1; i < argc; i++) {
834 		if (strchr(argv[i], '|') != NULL) {
835 			warnx("%s contains illegal character", argv[i]);
836 			unlink(path);
837 			exit(EX_SOFTWARE);
838 		}
839 		fprintf(fp, "%s%s", i == 1 ? "" : "|", argv[i]);
840 	}
841 
842 	fprintf(fp, "\n");
843 
844 	while ((ch = fgetc(ifile)) != EOF)
845 		if (fputc(ch, fp) == EOF) {
846 			warn("write error");
847 			unlink(path);
848 			exit(EX_UNAVAILABLE);
849 		}
850 
851 	if (ferror(ifile)) {
852 		warn("read error");
853 		unlink(path);
854 		exit(EX_UNAVAILABLE);
855 	}
856 
857 	fclose(fp);
858 
859 	return (EX_TEMPFAIL);
860 }
861