xref: /openbsd-src/usr.sbin/smtpd/enqueue.c (revision ffb4dd050d1e35f39b1d6c1c600db7c6443475c2)
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(&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 		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