xref: /openbsd-src/usr.sbin/smtpd/mda.c (revision 510586ace9b1e48cf6ab0f5a87b7657547e6cf37)
1*510586acSclaudio /*	$OpenBSD: mda.c,v 1.147 2024/01/20 09:01:03 claudio Exp $	*/
21f3c2bcfSsobrado 
33ef9cbf7Sgilles /*
465c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6e5b07014Sgilles  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7719ffb95Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
83ef9cbf7Sgilles  *
93ef9cbf7Sgilles  * Permission to use, copy, modify, and distribute this software for any
103ef9cbf7Sgilles  * purpose with or without fee is hereby granted, provided that the above
113ef9cbf7Sgilles  * copyright notice and this permission notice appear in all copies.
123ef9cbf7Sgilles  *
133ef9cbf7Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
143ef9cbf7Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
153ef9cbf7Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
163ef9cbf7Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
173ef9cbf7Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
183ef9cbf7Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
193ef9cbf7Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
203ef9cbf7Sgilles  */
213ef9cbf7Sgilles 
22a7b2e833Seric #include <ctype.h>
23c50073caSchl #include <inttypes.h>
243ef9cbf7Sgilles #include <stdlib.h>
25a05ec33aSgilles #include <string.h>
26eb268927Sgilles #include <sysexits.h>
270dcffd0dSop #include <time.h>
283ef9cbf7Sgilles #include <unistd.h>
291743d5a3Sjacekm #include <vis.h>
303ef9cbf7Sgilles 
313ef9cbf7Sgilles #include "smtpd.h"
325eb8dddaSgilles #include "log.h"
333ef9cbf7Sgilles 
34719ffb95Seric #define MDA_HIWAT		65536
35719ffb95Seric 
36985341b1Seric struct mda_envelope {
37985341b1Seric 	TAILQ_ENTRY(mda_envelope)	 entry;
381e265b4eSgilles 	uint64_t			 session_id;
39985341b1Seric 	uint64_t			 id;
40985341b1Seric 	time_t				 creation;
41985341b1Seric 	char				*sender;
42985341b1Seric 	char				*rcpt;
43a8e22235Sgilles 	char				*dest;
44985341b1Seric 	char				*user;
45a8e22235Sgilles 	char				*dispatcher;
46f67bab36Sgilles 	char				*mda_subaddress;
47a8e22235Sgilles 	char				*mda_exec;
48985341b1Seric };
49985341b1Seric 
507eed50e8Seric #define USER_WAITINFO	0x01
517eed50e8Seric #define USER_RUNNABLE	0x02
527eed50e8Seric #define USER_ONHOLD	0x04
537eed50e8Seric #define USER_HOLDQ	0x08
54985341b1Seric 
55dc65775cSeric struct mda_user {
567eed50e8Seric 	uint64_t			id;
57dc65775cSeric 	TAILQ_ENTRY(mda_user)		entry;
58793ff23cSeric 	TAILQ_ENTRY(mda_user)		entry_runnable;
59953aae25Sderaadt 	char				name[LOGIN_NAME_MAX];
60953aae25Sderaadt 	char				usertable[PATH_MAX];
61793ff23cSeric 	size_t				evpcount;
62985341b1Seric 	TAILQ_HEAD(, mda_envelope)	envelopes;
63985341b1Seric 	int				flags;
64dc65775cSeric 	size_t				running;
6565c4fdfbSgilles 	struct userinfo			userinfo;
66dc65775cSeric };
67dc65775cSeric 
68793ff23cSeric struct mda_session {
6965c4fdfbSgilles 	uint64_t		 id;
70793ff23cSeric 	struct mda_user		*user;
71985341b1Seric 	struct mda_envelope	*evp;
728d3f7f0dSeric 	struct io		*io;
73793ff23cSeric 	FILE			*datafp;
74793ff23cSeric };
75793ff23cSeric 
76b556a8d3Seric static void mda_io(struct io *, int, void *);
77985341b1Seric static int mda_check_loop(FILE *, struct mda_envelope *);
78719ffb95Seric static int mda_getlastline(int, char *, size_t);
7965c4fdfbSgilles static void mda_done(struct mda_session *);
8063b53c7eSsunil static void mda_fail(struct mda_user *, int, const char *,
8163b53c7eSsunil     enum enhanced_status_code);
82793ff23cSeric static void mda_drain(void);
83985341b1Seric static void mda_log(const struct mda_envelope *, const char *, const char *);
84aa1d5973Seric static void mda_queue_ok(uint64_t);
8563b53c7eSsunil static void mda_queue_tempfail(uint64_t, const char *,
8663b53c7eSsunil     enum enhanced_status_code);
87aa1d5973Seric static void mda_queue_permfail(uint64_t, const char *, enum enhanced_status_code);
88aa1d5973Seric static void mda_queue_loop(uint64_t);
897eed50e8Seric static struct mda_user *mda_user(const struct envelope *);
907eed50e8Seric static void mda_user_free(struct mda_user *);
917eed50e8Seric static const char *mda_user_to_text(const struct mda_user *);
921e265b4eSgilles static struct mda_envelope *mda_envelope(uint64_t, const struct envelope *);
937eed50e8Seric static void mda_envelope_free(struct mda_envelope *);
947eed50e8Seric static struct mda_session * mda_session(struct mda_user *);
95eb268927Sgilles static const char *mda_sysexit_to_str(int);
961743d5a3Sjacekm 
9776d9cc75Seric static struct tree	sessions;
987eed50e8Seric static struct tree	users;
99e5b07014Sgilles 
100793ff23cSeric static TAILQ_HEAD(, mda_user)	runnable;
101dc65775cSeric 
102aa1d5973Seric void
mda_imsg(struct mproc * p,struct imsg * imsg)10365c4fdfbSgilles mda_imsg(struct mproc *p, struct imsg *imsg)
1043ef9cbf7Sgilles {
105ed1929b6Sjacekm 	struct mda_session	*s;
106793ff23cSeric 	struct mda_user		*u;
107985341b1Seric 	struct mda_envelope	*e;
108985341b1Seric 	struct envelope		 evp;
10965c4fdfbSgilles 	struct deliver		 deliver;
11065c4fdfbSgilles 	struct msg		 m;
11165c4fdfbSgilles 	const void		*data;
112eb268927Sgilles 	const char		*error, *parent_error, *syserror;
11365c4fdfbSgilles 	uint64_t		 reqid;
11465c4fdfbSgilles 	size_t			 sz;
115953aae25Sderaadt 	char			 out[256], buf[LINE_MAX];
116*510586acSclaudio 	int			 n, fd;
117985341b1Seric 	enum lka_resp_status	status;
118eb268927Sgilles 	enum mda_resp_status	mda_status;
119eb268927Sgilles 	int			mda_sysexit;
120ed1929b6Sjacekm 
12165c4fdfbSgilles 	switch (imsg->hdr.type) {
122aa1d5973Seric 	case IMSG_MDA_LOOKUP_USERINFO:
12365c4fdfbSgilles 		m_msg(&m, imsg);
1247eed50e8Seric 		m_get_id(&m, &reqid);
125985341b1Seric 		m_get_int(&m, (int *)&status);
12665c4fdfbSgilles 		if (status == LKA_OK)
12765c4fdfbSgilles 			m_get_data(&m, &data, &sz);
12865c4fdfbSgilles 		m_end(&m);
12965c4fdfbSgilles 
1307eed50e8Seric 		u = tree_xget(&users, reqid);
13165c4fdfbSgilles 
13265c4fdfbSgilles 		if (status == LKA_TEMPFAIL)
13365c4fdfbSgilles 			mda_fail(u, 0,
134fe95d8d0Seric 			    "Temporary failure in user lookup",
135fe95d8d0Seric 			    ESC_OTHER_ADDRESS_STATUS);
13665c4fdfbSgilles 		else if (status == LKA_PERMFAIL)
13765c4fdfbSgilles 			mda_fail(u, 1,
138fe95d8d0Seric 			    "Permanent failure in user lookup",
139fe95d8d0Seric 			    ESC_DESTINATION_MAILBOX_HAS_MOVED);
14065c4fdfbSgilles 		else {
141bc1ed85bSsunil 			if (sz != sizeof(u->userinfo))
142bc1ed85bSsunil 				fatalx("mda: userinfo size mismatch");
14365c4fdfbSgilles 			memmove(&u->userinfo, data, sz);
1447eed50e8Seric 			u->flags &= ~USER_WAITINFO;
1457eed50e8Seric 			u->flags |= USER_RUNNABLE;
14665c4fdfbSgilles 			TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
14765c4fdfbSgilles 			mda_drain();
14865c4fdfbSgilles 		}
14965c4fdfbSgilles 		return;
150793ff23cSeric 
151aa1d5973Seric 	case IMSG_QUEUE_DELIVER:
15265c4fdfbSgilles 		m_msg(&m, imsg);
153985341b1Seric 		m_get_envelope(&m, &evp);
15465c4fdfbSgilles 		m_end(&m);
155793ff23cSeric 
1567eed50e8Seric 		u = mda_user(&evp);
157985341b1Seric 
1587eed50e8Seric 		if (u->evpcount >= env->sc_mda_task_hiwat) {
1597eed50e8Seric 			if (!(u->flags & USER_ONHOLD)) {
1607eed50e8Seric 				log_debug("debug: mda: hiwat reached for "
1617eed50e8Seric 				    "user \"%s\": holding envelopes",
1627eed50e8Seric 				    mda_user_to_text(u));
1637eed50e8Seric 				u->flags |= USER_ONHOLD;
1647eed50e8Seric 			}
1657eed50e8Seric 		}
1667eed50e8Seric 
1677eed50e8Seric 		if (u->flags & USER_ONHOLD) {
1687eed50e8Seric 			u->flags |= USER_HOLDQ;
16963b53c7eSsunil 			m_create(p_queue, IMSG_MDA_DELIVERY_HOLD,
17063b53c7eSsunil 			    0, 0, -1);
1717eed50e8Seric 			m_add_evpid(p_queue, evp.id);
1727eed50e8Seric 			m_add_id(p_queue, u->id);
1737eed50e8Seric 			m_close(p_queue);
174793ff23cSeric 			return;
175793ff23cSeric 		}
176793ff23cSeric 
1771e265b4eSgilles 		e = mda_envelope(u->id, &evp);
1787eed50e8Seric 		TAILQ_INSERT_TAIL(&u->envelopes, e, entry);
1797eed50e8Seric 		u->evpcount += 1;
180793ff23cSeric 		stat_increment("mda.pending", 1);
181793ff23cSeric 
1827eed50e8Seric 		if (!(u->flags & USER_RUNNABLE) &&
1837eed50e8Seric 		    !(u->flags & USER_WAITINFO)) {
1847eed50e8Seric 			u->flags |= USER_RUNNABLE;
1857eed50e8Seric 			TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
1867eed50e8Seric 		}
187985341b1Seric 
188793ff23cSeric 		mda_drain();
189793ff23cSeric 		return;
190793ff23cSeric 
191aa1d5973Seric 	case IMSG_MDA_OPEN_MESSAGE:
19265c4fdfbSgilles 		m_msg(&m, imsg);
19365c4fdfbSgilles 		m_get_id(&m, &reqid);
19465c4fdfbSgilles 		m_end(&m);
19565c4fdfbSgilles 
19665c4fdfbSgilles 		s = tree_xget(&sessions, reqid);
19765c4fdfbSgilles 		e = s->evp;
198793ff23cSeric 
199*510586acSclaudio 		fd = imsg_get_fd(imsg);
200*510586acSclaudio 		if (fd == -1) {
20182614934Seric 			log_debug("debug: mda: cannot get message fd");
20263b53c7eSsunil 			mda_queue_tempfail(e->id,
20363b53c7eSsunil 			    "Cannot get message fd",
204fe95d8d0Seric 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
205985341b1Seric 			mda_log(e, "TempFail", "Cannot get message fd");
20665c4fdfbSgilles 			mda_done(s);
207719ffb95Seric 			return;
208719ffb95Seric 		}
209a7b2e833Seric 
210d7bcae4dSeric 		log_debug("debug: mda: got message fd %d "
211985341b1Seric 		    "for session %016"PRIx64 " evpid %016"PRIx64,
212*510586acSclaudio 		    fd, s->id, e->id);
213985341b1Seric 
214*510586acSclaudio 		if ((s->datafp = fdopen(fd, "r")) == NULL) {
21582614934Seric 			log_warn("warn: mda: fdopen");
216*510586acSclaudio 			close(fd);
217aa1d5973Seric 			mda_queue_tempfail(e->id, "fdopen failed",
218fe95d8d0Seric 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
21965c4fdfbSgilles 			mda_log(e, "TempFail", "fdopen failed");
22065c4fdfbSgilles 			mda_done(s);
221719ffb95Seric 			return;
222719ffb95Seric 		}
223719ffb95Seric 
224719ffb95Seric 		/* check delivery loop */
225985341b1Seric 		if (mda_check_loop(s->datafp, e)) {
22682614934Seric 			log_debug("debug: mda: loop detected");
227aa1d5973Seric 			mda_queue_loop(e->id);
22865c4fdfbSgilles 			mda_log(e, "PermFail", "Loop detected");
22965c4fdfbSgilles 			mda_done(s);
230a7b2e833Seric 			return;
231a7b2e833Seric 		}
232a7b2e833Seric 
233719ffb95Seric 		/* start queueing delivery headers */
2344809a514Sgilles 		if (e->sender[0])
23563b53c7eSsunil 			/*
23663b53c7eSsunil 			 * XXX: remove existing Return-Path,
23763b53c7eSsunil 			 * if any
23863b53c7eSsunil 			 */
2398d3f7f0dSeric 			n = io_printf(s->io,
2404ddc25d9Sgilles 			    "Return-Path: <%s>\n"
2410173e407Schrisz 			    "Delivered-To: %s\n",
24263b53c7eSsunil 			    e->sender,
24363b53c7eSsunil 			    e->rcpt ? e->rcpt : e->dest);
244719ffb95Seric 		else
2458d3f7f0dSeric 			n = io_printf(s->io,
246985341b1Seric 			    "Delivered-To: %s\n",
247985341b1Seric 			    e->rcpt ? e->rcpt : e->dest);
248719ffb95Seric 		if (n == -1) {
24965c4fdfbSgilles 			log_warn("warn: mda: "
25065c4fdfbSgilles 			    "fail to write delivery info");
251aa1d5973Seric 			mda_queue_tempfail(e->id, "Out of memory",
252fe95d8d0Seric 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
253985341b1Seric 			mda_log(e, "TempFail", "Out of memory");
25465c4fdfbSgilles 			mda_done(s);
255719ffb95Seric 			return;
256719ffb95Seric 		}
257719ffb95Seric 
258e5b07014Sgilles 		/* request parent to fork a helper process */
259c1392a69Seric 		memset(&deliver, 0, sizeof deliver);
2608ba4ad8bSgilles 		(void)text_to_mailaddr(&deliver.sender, s->evp->sender);
2618ba4ad8bSgilles 		(void)text_to_mailaddr(&deliver.rcpt, s->evp->rcpt);
2628ba4ad8bSgilles 		(void)text_to_mailaddr(&deliver.dest, s->evp->dest);
263a8e22235Sgilles 		if (s->evp->mda_exec)
264a8e22235Sgilles 			(void)strlcpy(deliver.mda_exec, s->evp->mda_exec, sizeof deliver.mda_exec);
265f67bab36Sgilles 		if (s->evp->mda_subaddress)
266f67bab36Sgilles 			(void)strlcpy(deliver.mda_subaddress, s->evp->mda_subaddress, sizeof deliver.mda_subaddress);
267a8e22235Sgilles 		(void)strlcpy(deliver.dispatcher, s->evp->dispatcher, sizeof deliver.dispatcher);
268a8e22235Sgilles 		deliver.userinfo = s->user->userinfo;
269e5b07014Sgilles 
270985341b1Seric 		log_debug("debug: mda: querying mda fd "
271985341b1Seric 		    "for session %016"PRIx64 " evpid %016"PRIx64,
272985341b1Seric 		    s->id, s->evp->id);
273985341b1Seric 
274aa1d5973Seric 		m_create(p_parent, IMSG_MDA_FORK, 0, 0, -1);
27565c4fdfbSgilles 		m_add_id(p_parent, reqid);
27665c4fdfbSgilles 		m_add_data(p_parent, &deliver, sizeof(deliver));
27765c4fdfbSgilles 		m_close(p_parent);
2783ef9cbf7Sgilles 		return;
2791eb29730Sjacekm 
280aa1d5973Seric 	case IMSG_MDA_FORK:
28165c4fdfbSgilles 		m_msg(&m, imsg);
28265c4fdfbSgilles 		m_get_id(&m, &reqid);
28365c4fdfbSgilles 		m_end(&m);
28465c4fdfbSgilles 
28565c4fdfbSgilles 		s = tree_xget(&sessions, reqid);
28665c4fdfbSgilles 		e = s->evp;
287*510586acSclaudio 		fd = imsg_get_fd(imsg);
288*510586acSclaudio 		if (fd == -1) {
289b317af72Snicm 			log_warn("warn: mda: fail to retrieve mda fd");
290aa1d5973Seric 			mda_queue_tempfail(e->id, "Cannot get mda fd",
291fe95d8d0Seric 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
29265c4fdfbSgilles 			mda_log(e, "TempFail", "Cannot get mda fd");
29365c4fdfbSgilles 			mda_done(s);
294719ffb95Seric 			return;
295719ffb95Seric 		}
296719ffb95Seric 
297d7bcae4dSeric 		log_debug("debug: mda: got mda fd %d "
298985341b1Seric 		    "for session %016"PRIx64 " evpid %016"PRIx64,
299*510586acSclaudio 		    fd, s->id, s->evp->id);
300985341b1Seric 
301*510586acSclaudio 		io_set_nonblocking(fd);
302*510586acSclaudio 		io_set_fd(s->io, fd);
3038d3f7f0dSeric 		io_set_write(s->io);
304ed1929b6Sjacekm 		return;
3053ef9cbf7Sgilles 
306ed1929b6Sjacekm 	case IMSG_MDA_DONE:
30765c4fdfbSgilles 		m_msg(&m, imsg);
30865c4fdfbSgilles 		m_get_id(&m, &reqid);
309eb268927Sgilles 		m_get_int(&m, (int *)&mda_status);
310eb268927Sgilles 		m_get_int(&m, (int *)&mda_sysexit);
31165c4fdfbSgilles 		m_get_string(&m, &parent_error);
31265c4fdfbSgilles 		m_end(&m);
31365c4fdfbSgilles 
31465c4fdfbSgilles 		s = tree_xget(&sessions, reqid);
315985341b1Seric 		e = s->evp;
3161743d5a3Sjacekm 		/*
3171743d5a3Sjacekm 		 * Grab last line of mda stdout/stderr if available.
3181743d5a3Sjacekm 		 */
31965c4fdfbSgilles 		out[0] = '\0';
320*510586acSclaudio 		fd = imsg_get_fd(imsg);
321*510586acSclaudio 		if (fd != -1)
322*510586acSclaudio 			mda_getlastline(fd, out, sizeof(out));
323eb268927Sgilles 
32411dbc40fSjacekm 		/*
3251743d5a3Sjacekm 		 * Choose between parent's description of error and
3261743d5a3Sjacekm 		 * child's output, the latter having preference over
3271743d5a3Sjacekm 		 * the former.
32811dbc40fSjacekm 		 */
329e5b07014Sgilles 		error = NULL;
330eb268927Sgilles 		if (mda_status == MDA_OK) {
331eb268927Sgilles 			if (s->datafp || (s->io && io_queued(s->io))) {
332e5b07014Sgilles 				error = "mda exited prematurely";
333eb268927Sgilles 				mda_status = MDA_TEMPFAIL;
334eb268927Sgilles 			}
335719ffb95Seric 		} else
33665c4fdfbSgilles 			error = out[0] ? out : parent_error;
3373ef9cbf7Sgilles 
338eb268927Sgilles 		syserror = NULL;
339e570109dSgilles 		if (mda_sysexit)
340eb268927Sgilles 			syserror = mda_sysexit_to_str(mda_sysexit);
341eb268927Sgilles 
3421743d5a3Sjacekm 		/* update queue entry */
343eb268927Sgilles 		switch (mda_status) {
344eb268927Sgilles 		case MDA_TEMPFAIL:
345aa1d5973Seric 			mda_queue_tempfail(e->id, error,
346fe95d8d0Seric 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
34763b53c7eSsunil 			(void)snprintf(buf, sizeof buf,
348e570109dSgilles 			    "Error (%s%s%s)",
349e570109dSgilles 				       syserror ? syserror : "",
350e570109dSgilles 				       syserror ? ": " : "",
351e570109dSgilles 				       error);
352985341b1Seric 			mda_log(e, "TempFail", buf);
353eb268927Sgilles 			break;
354eb268927Sgilles 		case MDA_PERMFAIL:
355eb268927Sgilles 			mda_queue_permfail(e->id, error,
356eb268927Sgilles 			    ESC_OTHER_MAIL_SYSTEM_STATUS);
357eb268927Sgilles 			(void)snprintf(buf, sizeof buf,
358e570109dSgilles 			    "Error (%s%s%s)",
359e570109dSgilles 				       syserror ? syserror : "",
360e570109dSgilles 				       syserror ? ": " : "",
361e570109dSgilles 				       error);
362eb268927Sgilles 			mda_log(e, "PermFail", buf);
363eb268927Sgilles 			break;
364eb268927Sgilles 		case MDA_OK:
365aa1d5973Seric 			mda_queue_ok(e->id);
366985341b1Seric 			mda_log(e, "Ok", "Delivered");
367eb268927Sgilles 			break;
36865c4fdfbSgilles 		}
36965c4fdfbSgilles 		mda_done(s);
370ed1929b6Sjacekm 		return;
3713ef9cbf7Sgilles 	}
3721eb29730Sjacekm 
373ff01b044Seric 	fatalx("mda_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
374af8de4b6Sgilles }
375af8de4b6Sgilles 
376aa1d5973Seric void
mda_postfork(void)37704fee684Stb mda_postfork(void)
378af8de4b6Sgilles {
3793ef9cbf7Sgilles }
3803ef9cbf7Sgilles 
381aa1d5973Seric void
mda_postprivdrop(void)38204fee684Stb mda_postprivdrop(void)
3833ef9cbf7Sgilles {
38476d9cc75Seric 	tree_init(&sessions);
3857eed50e8Seric 	tree_init(&users);
386793ff23cSeric 	TAILQ_INIT(&runnable);
3873ef9cbf7Sgilles }
3883ef9cbf7Sgilles 
389be925435Sgilles static void
mda_io(struct io * io,int evt,void * arg)390b556a8d3Seric mda_io(struct io *io, int evt, void *arg)
3913ef9cbf7Sgilles {
392b556a8d3Seric 	struct mda_session	*s = arg;
393cec67479Ssunil 	char			*ln = NULL;
394cec67479Ssunil 	size_t			 sz = 0;
395cec67479Ssunil 	ssize_t			 len;
396689fc259Sjacekm 
39765c4fdfbSgilles 	log_trace(TRACE_IO, "mda: %p: %s %s", s, io_strevent(evt),
39865c4fdfbSgilles 	    io_strio(io));
39989953979Sjacekm 
400719ffb95Seric 	switch (evt) {
401719ffb95Seric 	case IO_LOWAT:
402719ffb95Seric 
403719ffb95Seric 	/* done */
404299c4efeSeric 	done:
405719ffb95Seric 		if (s->datafp == NULL) {
406985341b1Seric 			log_debug("debug: mda: all data sent for session"
407985341b1Seric 			    " %016"PRIx64 " evpid %016"PRIx64,
408985341b1Seric 			    s->id, s->evp->id);
4098d3f7f0dSeric 			io_free(io);
4108d3f7f0dSeric 			s->io = NULL;
41189953979Sjacekm 			return;
41289953979Sjacekm 		}
41389953979Sjacekm 
4148d3f7f0dSeric 		while (io_queued(s->io) < MDA_HIWAT) {
415cec67479Ssunil 			if ((len = getline(&ln, &sz, s->datafp)) == -1)
416719ffb95Seric 				break;
4178d3f7f0dSeric 			if (io_write(s->io, ln, len) == -1) {
418aa1d5973Seric 				m_create(p_parent, IMSG_MDA_KILL,
419299c4efeSeric 				    0, 0, -1);
42065c4fdfbSgilles 				m_add_id(p_parent, s->id);
42165c4fdfbSgilles 				m_add_string(p_parent, "Out of memory");
42265c4fdfbSgilles 				m_close(p_parent);
423f80a744fSeric 				io_pause(io, IO_OUT);
424cec67479Ssunil 				free(ln);
425719ffb95Seric 				return;
426719ffb95Seric 			}
427719ffb95Seric 		}
428719ffb95Seric 
429cec67479Ssunil 		free(ln);
430a0d73992Sjsg 		ln = NULL;
431719ffb95Seric 		if (ferror(s->datafp)) {
432985341b1Seric 			log_debug("debug: mda: ferror on session %016"PRIx64,
433985341b1Seric 			    s->id);
434aa1d5973Seric 			m_create(p_parent, IMSG_MDA_KILL, 0, 0, -1);
43565c4fdfbSgilles 			m_add_id(p_parent, s->id);
43665c4fdfbSgilles 			m_add_string(p_parent, "Error reading body");
43765c4fdfbSgilles 			m_close(p_parent);
438f80a744fSeric 			io_pause(io, IO_OUT);
439719ffb95Seric 			return;
440719ffb95Seric 		}
441719ffb95Seric 
442719ffb95Seric 		if (feof(s->datafp)) {
443985341b1Seric 			log_debug("debug: mda: end-of-file for session"
444985341b1Seric 			    " %016"PRIx64 " evpid %016"PRIx64,
445985341b1Seric 			    s->id, s->evp->id);
446719ffb95Seric 			fclose(s->datafp);
447719ffb95Seric 			s->datafp = NULL;
4488d3f7f0dSeric 			if (io_queued(s->io) == 0)
449299c4efeSeric 				goto done;
450719ffb95Seric 		}
451719ffb95Seric 		return;
452719ffb95Seric 
453719ffb95Seric 	case IO_TIMEOUT:
454985341b1Seric 		log_debug("debug: mda: timeout on session %016"PRIx64, s->id);
455f80a744fSeric 		io_pause(io, IO_OUT);
456719ffb95Seric 		return;
457719ffb95Seric 
458719ffb95Seric 	case IO_ERROR:
459985341b1Seric 		log_debug("debug: mda: io error on session %016"PRIx64": %s",
460219e2fd6Seric 		    s->id, io_error(io));
461f80a744fSeric 		io_pause(io, IO_OUT);
462719ffb95Seric 		return;
463719ffb95Seric 
464719ffb95Seric 	case IO_DISCONNECTED:
465985341b1Seric 		log_debug("debug: mda: io disconnected on session %016"PRIx64,
466985341b1Seric 		    s->id);
467f80a744fSeric 		io_pause(io, IO_OUT);
468719ffb95Seric 		return;
469719ffb95Seric 
470719ffb95Seric 	default:
471985341b1Seric 		log_debug("debug: mda: unexpected event on session %016"PRIx64,
472985341b1Seric 		    s->id);
473f80a744fSeric 		io_pause(io, IO_OUT);
474719ffb95Seric 		return;
475719ffb95Seric 	}
476689fc259Sjacekm }
477689fc259Sjacekm 
478a7b2e833Seric static int
mda_check_loop(FILE * fp,struct mda_envelope * e)479985341b1Seric mda_check_loop(FILE *fp, struct mda_envelope *e)
480a7b2e833Seric {
481cec67479Ssunil 	char		*buf = NULL;
482cec67479Ssunil 	size_t		 sz = 0;
483cec67479Ssunil 	ssize_t		 len;
484a7b2e833Seric 	int		 ret = 0;
485a7b2e833Seric 
486cec67479Ssunil 	while ((len = getline(&buf, &sz, fp)) != -1) {
487a7b2e833Seric 		if (buf[len - 1] == '\n')
488a7b2e833Seric 			buf[len - 1] = '\0';
489a7b2e833Seric 
490fc3a8311Seric 		if (strchr(buf, ':') == NULL && !isspace((unsigned char)*buf))
491a7b2e833Seric 			break;
492a7b2e833Seric 
493a7b2e833Seric 		if (strncasecmp("Delivered-To: ", buf, 14) == 0) {
494985341b1Seric 			if (strcasecmp(buf + 14, e->dest) == 0) {
495a7b2e833Seric 				ret = 1;
496a7b2e833Seric 				break;
497a7b2e833Seric 			}
498a7b2e833Seric 		}
499a7b2e833Seric 	}
500a7b2e833Seric 
501cec67479Ssunil 	free(buf);
502a7b2e833Seric 	fseek(fp, SEEK_SET, 0);
503a7b2e833Seric 	return (ret);
504a7b2e833Seric }
505dc65775cSeric 
506719ffb95Seric static int
mda_getlastline(int fd,char * dst,size_t dstsz)507719ffb95Seric mda_getlastline(int fd, char *dst, size_t dstsz)
508719ffb95Seric {
509719ffb95Seric 	FILE	*fp;
510cec67479Ssunil 	char	*ln = NULL;
511cec67479Ssunil 	size_t	 sz = 0;
512cec67479Ssunil 	ssize_t	 len;
51340837cf2Sgilles 	int	 out = 0;
514719ffb95Seric 
515df69c215Sderaadt 	if (lseek(fd, 0, SEEK_SET) == -1) {
51682614934Seric 		log_warn("warn: mda: lseek");
517719ffb95Seric 		close(fd);
518719ffb95Seric 		return (-1);
519719ffb95Seric 	}
520719ffb95Seric 	fp = fdopen(fd, "r");
521719ffb95Seric 	if (fp == NULL) {
52282614934Seric 		log_warn("warn: mda: fdopen");
523719ffb95Seric 		close(fd);
524719ffb95Seric 		return (-1);
525719ffb95Seric 	}
526cec67479Ssunil 	while ((len = getline(&ln, &sz, fp)) != -1) {
527719ffb95Seric 		if (ln[len - 1] == '\n')
528cec67479Ssunil 			ln[len - 1] = '\0';
52940837cf2Sgilles 		out = 1;
530719ffb95Seric 	}
531719ffb95Seric 	fclose(fp);
532719ffb95Seric 
53340837cf2Sgilles 	if (out) {
534d124c6e2Sgilles 		(void)strlcpy(dst, "\"", dstsz);
535a9d5072aSgilles 		(void)strnvis(dst + 1, ln, dstsz - 2, VIS_SAFE | VIS_CSTYLE | VIS_NL);
536d124c6e2Sgilles 		(void)strlcat(dst, "\"", dstsz);
537719ffb95Seric 	}
538719ffb95Seric 
539cec67479Ssunil 	free(ln);
540719ffb95Seric 	return (0);
541719ffb95Seric }
542719ffb95Seric 
543793ff23cSeric static void
mda_fail(struct mda_user * user,int permfail,const char * error,enum enhanced_status_code code)54463b53c7eSsunil mda_fail(struct mda_user *user, int permfail, const char *error,
54563b53c7eSsunil     enum enhanced_status_code code)
54665c4fdfbSgilles {
547985341b1Seric 	struct mda_envelope	*e;
54865c4fdfbSgilles 
54965c4fdfbSgilles 	while ((e = TAILQ_FIRST(&user->envelopes))) {
55065c4fdfbSgilles 		TAILQ_REMOVE(&user->envelopes, e, entry);
55165c4fdfbSgilles 		if (permfail) {
55265c4fdfbSgilles 			mda_log(e, "PermFail", error);
553aa1d5973Seric 			mda_queue_permfail(e->id, error, code);
55465c4fdfbSgilles 		}
55565c4fdfbSgilles 		else {
55665c4fdfbSgilles 			mda_log(e, "TempFail", error);
557aa1d5973Seric 			mda_queue_tempfail(e->id, error, code);
55865c4fdfbSgilles 		}
5597eed50e8Seric 		mda_envelope_free(e);
56065c4fdfbSgilles 	}
56165c4fdfbSgilles 
5627eed50e8Seric 	mda_user_free(user);
56365c4fdfbSgilles }
56465c4fdfbSgilles 
56565c4fdfbSgilles static void
mda_drain(void)566793ff23cSeric mda_drain(void)
567dc65775cSeric {
568985341b1Seric 	struct mda_user		*u;
569dc65775cSeric 
570985341b1Seric 	while ((u = (TAILQ_FIRST(&runnable)))) {
5717eed50e8Seric 
572985341b1Seric 		TAILQ_REMOVE(&runnable, u, entry_runnable);
573985341b1Seric 
574985341b1Seric 		if (u->evpcount == 0 && u->running == 0) {
575985341b1Seric 			log_debug("debug: mda: all done for user \"%s\"",
5767eed50e8Seric 			    mda_user_to_text(u));
5777eed50e8Seric 			mda_user_free(u);
578985341b1Seric 			continue;
579985341b1Seric 		}
580985341b1Seric 
581985341b1Seric 		if (u->evpcount == 0) {
582985341b1Seric 			log_debug("debug: mda: no more envelope for \"%s\"",
5837eed50e8Seric 			    mda_user_to_text(u));
5847eed50e8Seric 			u->flags &= ~USER_RUNNABLE;
585985341b1Seric 			continue;
586985341b1Seric 		}
587985341b1Seric 
5887eed50e8Seric 		if (u->running >= env->sc_mda_max_user_session) {
589985341b1Seric 			log_debug("debug: mda: "
590985341b1Seric 			    "maximum number of session reached for user \"%s\"",
5917eed50e8Seric 			    mda_user_to_text(u));
5927eed50e8Seric 			u->flags &= ~USER_RUNNABLE;
593985341b1Seric 			continue;
594985341b1Seric 		}
595793ff23cSeric 
5967eed50e8Seric 		if (tree_count(&sessions) >= env->sc_mda_max_session) {
59765c4fdfbSgilles 			log_debug("debug: mda: "
59865c4fdfbSgilles 			    "maximum number of session reached");
599985341b1Seric 			TAILQ_INSERT_HEAD(&runnable, u, entry_runnable);
600793ff23cSeric 			return;
601dc65775cSeric 		}
602dc65775cSeric 
6037eed50e8Seric 		mda_session(u);
60465c4fdfbSgilles 
6057eed50e8Seric 		if (u->evpcount == env->sc_mda_task_lowat) {
6067eed50e8Seric 			if (u->flags & USER_ONHOLD) {
60763b53c7eSsunil 				log_debug("debug: mda: down to lowat for user "
60863b53c7eSsunil 				    "\"%s\": releasing",
6097eed50e8Seric 				    mda_user_to_text(u));
6107eed50e8Seric 				u->flags &= ~USER_ONHOLD;
6117eed50e8Seric 			}
6127eed50e8Seric 			if (u->flags & USER_HOLDQ) {
61363b53c7eSsunil 				m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE,
61463b53c7eSsunil 				    0, 0, -1);
6157eed50e8Seric 				m_add_id(p_queue, u->id);
6167eed50e8Seric 				m_add_int(p_queue, env->sc_mda_task_release);
61765c4fdfbSgilles 				m_close(p_queue);
6187eed50e8Seric 			}
6197eed50e8Seric 		}
620793ff23cSeric 
6217eed50e8Seric 		/* re-add the user at the tail of the queue */
622985341b1Seric 		TAILQ_INSERT_TAIL(&runnable, u, entry_runnable);
623793ff23cSeric 	}
624dc65775cSeric }
625dc65775cSeric 
626dc65775cSeric static void
mda_done(struct mda_session * s)62765c4fdfbSgilles mda_done(struct mda_session *s)
628dc65775cSeric {
6297eed50e8Seric 	log_debug("debug: mda: session %016" PRIx64 " done", s->id);
630985341b1Seric 
631793ff23cSeric 	tree_xpop(&sessions, s->id);
632dc65775cSeric 
6337eed50e8Seric 	mda_envelope_free(s->evp);
634985341b1Seric 
6357eed50e8Seric 	s->user->running--;
6367eed50e8Seric 	if (!(s->user->flags & USER_RUNNABLE)) {
6377eed50e8Seric 		log_debug("debug: mda: user \"%s\" becomes runnable",
6387eed50e8Seric 		    s->user->name);
6397eed50e8Seric 		TAILQ_INSERT_TAIL(&runnable, s->user, entry_runnable);
6407eed50e8Seric 		s->user->flags |= USER_RUNNABLE;
6417eed50e8Seric 	}
642793ff23cSeric 
643793ff23cSeric 	if (s->datafp)
644793ff23cSeric 		fclose(s->datafp);
6458d3f7f0dSeric 	if (s->io)
6468d3f7f0dSeric 		io_free(s->io);
647985341b1Seric 
648793ff23cSeric 	free(s);
649985341b1Seric 
6507eed50e8Seric 	stat_decrement("mda.running", 1);
651793ff23cSeric 
652793ff23cSeric 	mda_drain();
653dc65775cSeric }
65465c4fdfbSgilles 
65565c4fdfbSgilles static void
mda_log(const struct mda_envelope * evp,const char * prefix,const char * status)656985341b1Seric mda_log(const struct mda_envelope *evp, const char *prefix, const char *status)
65765c4fdfbSgilles {
658953aae25Sderaadt 	char rcpt[LINE_MAX];
65965c4fdfbSgilles 
66065c4fdfbSgilles 	rcpt[0] = '\0';
661985341b1Seric 	if (evp->rcpt)
662ee64380fSgilles 		(void)snprintf(rcpt, sizeof rcpt, "rcpt=<%s> ", evp->rcpt);
66365c4fdfbSgilles 
664cf540174Sgilles 	log_info("%016"PRIx64" mda delivery evpid=%016" PRIx64 " from=<%s> to=<%s> "
665a8e22235Sgilles 	    "%suser=%s delay=%s result=%s stat=%s",
6661e265b4eSgilles 	    evp->session_id,
667e55799c3Seric 	    evp->id,
668985341b1Seric 	    evp->sender ? evp->sender : "",
669985341b1Seric 	    evp->dest,
67065c4fdfbSgilles 	    rcpt,
671985341b1Seric 	    evp->user,
67265c4fdfbSgilles 	    duration_to_text(time(NULL) - evp->creation),
6737a93bdaaSgilles 	    prefix,
67465c4fdfbSgilles 	    status);
67565c4fdfbSgilles }
6767eed50e8Seric 
677aa1d5973Seric static void
mda_queue_ok(uint64_t evpid)678aa1d5973Seric mda_queue_ok(uint64_t evpid)
679aa1d5973Seric {
680aa1d5973Seric 	m_create(p_queue, IMSG_MDA_DELIVERY_OK, 0, 0, -1);
681aa1d5973Seric 	m_add_evpid(p_queue, evpid);
682aa1d5973Seric 	m_close(p_queue);
683aa1d5973Seric }
684aa1d5973Seric 
685aa1d5973Seric static void
mda_queue_tempfail(uint64_t evpid,const char * reason,enum enhanced_status_code code)68663b53c7eSsunil mda_queue_tempfail(uint64_t evpid, const char *reason,
68763b53c7eSsunil     enum enhanced_status_code code)
688aa1d5973Seric {
689aa1d5973Seric 	m_create(p_queue, IMSG_MDA_DELIVERY_TEMPFAIL, 0, 0, -1);
690aa1d5973Seric 	m_add_evpid(p_queue, evpid);
691aa1d5973Seric 	m_add_string(p_queue, reason);
692aa1d5973Seric 	m_add_int(p_queue, (int)code);
693aa1d5973Seric 	m_close(p_queue);
694aa1d5973Seric }
695aa1d5973Seric 
696aa1d5973Seric static void
mda_queue_permfail(uint64_t evpid,const char * reason,enum enhanced_status_code code)69763b53c7eSsunil mda_queue_permfail(uint64_t evpid, const char *reason,
69863b53c7eSsunil     enum enhanced_status_code code)
699aa1d5973Seric {
700aa1d5973Seric 	m_create(p_queue, IMSG_MDA_DELIVERY_PERMFAIL, 0, 0, -1);
701aa1d5973Seric 	m_add_evpid(p_queue, evpid);
702aa1d5973Seric 	m_add_string(p_queue, reason);
703aa1d5973Seric 	m_add_int(p_queue, (int)code);
704aa1d5973Seric 	m_close(p_queue);
705aa1d5973Seric }
706aa1d5973Seric 
707aa1d5973Seric static void
mda_queue_loop(uint64_t evpid)708aa1d5973Seric mda_queue_loop(uint64_t evpid)
709aa1d5973Seric {
710aa1d5973Seric 	m_create(p_queue, IMSG_MDA_DELIVERY_LOOP, 0, 0, -1);
711aa1d5973Seric 	m_add_evpid(p_queue, evpid);
712aa1d5973Seric 	m_close(p_queue);
713aa1d5973Seric }
714aa1d5973Seric 
7157eed50e8Seric static struct mda_user *
mda_user(const struct envelope * evp)7167eed50e8Seric mda_user(const struct envelope *evp)
7177eed50e8Seric {
718a8e22235Sgilles 	struct dispatcher *dsp;
7197eed50e8Seric 	struct mda_user	*u;
7207eed50e8Seric 	void		*i;
7217eed50e8Seric 
7227eed50e8Seric 	i = NULL;
723a8e22235Sgilles 	dsp = dict_xget(env->sc_dispatchers, evp->dispatcher);
7247eed50e8Seric 	while (tree_iter(&users, &i, NULL, (void**)(&u))) {
725a8e22235Sgilles 		if (!strcmp(evp->mda_user, u->name) &&
726a8e22235Sgilles 		    !strcmp(dsp->u.local.table_userbase, u->usertable))
7277eed50e8Seric 			return (u);
7287eed50e8Seric 	}
7297eed50e8Seric 
730118c16f3Sgilles 	u = xcalloc(1, sizeof *u);
7317eed50e8Seric 	u->id = generate_uid();
7327eed50e8Seric 	TAILQ_INIT(&u->envelopes);
733a8e22235Sgilles 	(void)strlcpy(u->name, evp->mda_user, sizeof(u->name));
734a8e22235Sgilles 	(void)strlcpy(u->usertable, dsp->u.local.table_userbase,
7357eed50e8Seric 	    sizeof(u->usertable));
7367eed50e8Seric 
7377eed50e8Seric 	tree_xset(&users, u->id, u);
7387eed50e8Seric 
739aa1d5973Seric 	m_create(p_lka, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1);
7407eed50e8Seric 	m_add_id(p_lka, u->id);
741a8e22235Sgilles 	m_add_string(p_lka, dsp->u.local.table_userbase);
742a8e22235Sgilles 	m_add_string(p_lka, evp->mda_user);
7437eed50e8Seric 	m_close(p_lka);
7447eed50e8Seric 	u->flags |= USER_WAITINFO;
7457eed50e8Seric 
7467eed50e8Seric 	stat_increment("mda.user", 1);
7477eed50e8Seric 
748a8e22235Sgilles 	if (dsp->u.local.user)
74963b53c7eSsunil 		log_debug("mda: new user %016" PRIx64
75063b53c7eSsunil 		    " for \"%s\" delivering as \"%s\"",
751a8e22235Sgilles 		    u->id, mda_user_to_text(u), dsp->u.local.user);
7528e10b5b2Ssunil 	else
75363b53c7eSsunil 		log_debug("mda: new user %016" PRIx64
75463b53c7eSsunil 		    " for \"%s\"", u->id, mda_user_to_text(u));
7557eed50e8Seric 
7567eed50e8Seric 	return (u);
7577eed50e8Seric }
7587eed50e8Seric 
7597eed50e8Seric static void
mda_user_free(struct mda_user * u)7607eed50e8Seric mda_user_free(struct mda_user *u)
7617eed50e8Seric {
7627eed50e8Seric 	tree_xpop(&users, u->id);
7637eed50e8Seric 
7647eed50e8Seric 	if (u->flags & USER_HOLDQ) {
765aa1d5973Seric 		m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE, 0, 0, -1);
7667eed50e8Seric 		m_add_id(p_queue, u->id);
7677eed50e8Seric 		m_add_int(p_queue, 0);
7687eed50e8Seric 		m_close(p_queue);
7697eed50e8Seric 	}
7707eed50e8Seric 
7717eed50e8Seric 	free(u);
7727eed50e8Seric 	stat_decrement("mda.user", 1);
7737eed50e8Seric }
7747eed50e8Seric 
7757eed50e8Seric static const char *
mda_user_to_text(const struct mda_user * u)7767eed50e8Seric mda_user_to_text(const struct mda_user *u)
7777eed50e8Seric {
7787eed50e8Seric 	static char buf[1024];
7797eed50e8Seric 
78023c462a5Sgilles 	(void)snprintf(buf, sizeof(buf), "%s:%s", u->usertable, u->name);
7817eed50e8Seric 
7827eed50e8Seric 	return (buf);
7837eed50e8Seric }
7847eed50e8Seric 
7857eed50e8Seric static struct mda_envelope *
mda_envelope(uint64_t session_id,const struct envelope * evp)7861e265b4eSgilles mda_envelope(uint64_t session_id, const struct envelope *evp)
7877eed50e8Seric {
7887eed50e8Seric 	struct mda_envelope	*e;
789953aae25Sderaadt 	char			 buf[LINE_MAX];
7907eed50e8Seric 
791118c16f3Sgilles 	e = xcalloc(1, sizeof *e);
7921e265b4eSgilles 	e->session_id = session_id;
7937eed50e8Seric 	e->id = evp->id;
7947eed50e8Seric 	e->creation = evp->creation;
7957eed50e8Seric 	buf[0] = '\0';
7967eed50e8Seric 	if (evp->sender.user[0] && evp->sender.domain[0])
79723c462a5Sgilles 		(void)snprintf(buf, sizeof buf, "%s@%s",
7987eed50e8Seric 		    evp->sender.user, evp->sender.domain);
799118c16f3Sgilles 	e->sender = xstrdup(buf);
80063b53c7eSsunil 	(void)snprintf(buf, sizeof buf, "%s@%s", evp->dest.user,
80163b53c7eSsunil 	    evp->dest.domain);
802118c16f3Sgilles 	e->dest = xstrdup(buf);
80363b53c7eSsunil 	(void)snprintf(buf, sizeof buf, "%s@%s", evp->rcpt.user,
80463b53c7eSsunil 	    evp->rcpt.domain);
805118c16f3Sgilles 	e->rcpt = xstrdup(buf);
806a8e22235Sgilles 	e->user = evp->mda_user[0] ?
807118c16f3Sgilles 	    xstrdup(evp->mda_user) : xstrdup(evp->dest.user);
808118c16f3Sgilles 	e->dispatcher = xstrdup(evp->dispatcher);
809a8e22235Sgilles 	if (evp->mda_exec[0])
810118c16f3Sgilles 		e->mda_exec = xstrdup(evp->mda_exec);
811f67bab36Sgilles 	if (evp->mda_subaddress[0])
812f67bab36Sgilles 		e->mda_subaddress = xstrdup(evp->mda_subaddress);
8137eed50e8Seric 	stat_increment("mda.envelope", 1);
8147eed50e8Seric 	return (e);
8157eed50e8Seric }
8167eed50e8Seric 
8177eed50e8Seric static void
mda_envelope_free(struct mda_envelope * e)8187eed50e8Seric mda_envelope_free(struct mda_envelope *e)
8197eed50e8Seric {
8207eed50e8Seric 	free(e->sender);
8217eed50e8Seric 	free(e->dest);
8227eed50e8Seric 	free(e->rcpt);
8237eed50e8Seric 	free(e->user);
824a8e22235Sgilles 	free(e->mda_exec);
8257eed50e8Seric 	free(e);
8267eed50e8Seric 
8277eed50e8Seric 	stat_decrement("mda.envelope", 1);
8287eed50e8Seric }
8297eed50e8Seric 
8307eed50e8Seric static struct mda_session *
mda_session(struct mda_user * u)8317eed50e8Seric mda_session(struct mda_user * u)
8327eed50e8Seric {
8337eed50e8Seric 	struct mda_session *s;
8347eed50e8Seric 
835118c16f3Sgilles 	s = xcalloc(1, sizeof *s);
8367eed50e8Seric 	s->id = generate_uid();
8377eed50e8Seric 	s->user = u;
8388d3f7f0dSeric 	s->io = io_new();
8398d3f7f0dSeric 	io_set_callback(s->io, mda_io, s);
8407eed50e8Seric 
8417eed50e8Seric 	tree_xset(&sessions, s->id, s);
8427eed50e8Seric 
8437eed50e8Seric 	s->evp = TAILQ_FIRST(&u->envelopes);
8447eed50e8Seric 	TAILQ_REMOVE(&u->envelopes, s->evp, entry);
8457eed50e8Seric 	u->evpcount--;
8467eed50e8Seric 	u->running++;
8477eed50e8Seric 
8487eed50e8Seric 	stat_decrement("mda.pending", 1);
8497eed50e8Seric 	stat_increment("mda.running", 1);
8507eed50e8Seric 
8517eed50e8Seric 	log_debug("debug: mda: new session %016" PRIx64
8527eed50e8Seric 	    " for user \"%s\" evpid %016" PRIx64, s->id,
8537eed50e8Seric 	    mda_user_to_text(u), s->evp->id);
8547eed50e8Seric 
855aa1d5973Seric 	m_create(p_queue, IMSG_MDA_OPEN_MESSAGE, 0, 0, -1);
8567eed50e8Seric 	m_add_id(p_queue, s->id);
8577eed50e8Seric 	m_add_msgid(p_queue, evpid_to_msgid(s->evp->id));
8587eed50e8Seric 	m_close(p_queue);
8597eed50e8Seric 
8607eed50e8Seric 	return (s);
8617eed50e8Seric }
862eb268927Sgilles 
863eb268927Sgilles static const char *
mda_sysexit_to_str(int sysexit)864eb268927Sgilles mda_sysexit_to_str(int sysexit)
865eb268927Sgilles {
866eb268927Sgilles 	switch (sysexit) {
867eb268927Sgilles 	case EX_USAGE:
868eb268927Sgilles 		return "command line usage error";
869eb268927Sgilles 	case EX_DATAERR:
870eb268927Sgilles 		return "data format error";
871eb268927Sgilles 	case EX_NOINPUT:
872eb268927Sgilles 		return "cannot open input";
873eb268927Sgilles 	case EX_NOUSER:
874eb268927Sgilles 		return "user unknown";
875eb268927Sgilles 	case EX_NOHOST:
876eb268927Sgilles 		return "host name unknown";
877eb268927Sgilles 	case EX_UNAVAILABLE:
878eb268927Sgilles 		return "service unavailable";
879eb268927Sgilles 	case EX_SOFTWARE:
880eb268927Sgilles 		return "internal software error";
881eb268927Sgilles 	case EX_OSERR:
882eb268927Sgilles 		return "system resource problem";
883eb268927Sgilles 	case EX_OSFILE:
884eb268927Sgilles 		return "critical OS file missing";
885eb268927Sgilles 	case EX_CANTCREAT:
886eb268927Sgilles 		return "can't create user output file";
887eb268927Sgilles 	case EX_IOERR:
888eb268927Sgilles 		return "input/output error";
889eb268927Sgilles 	case EX_TEMPFAIL:
890eb268927Sgilles 		return "temporary failure";
891eb268927Sgilles 	case EX_PROTOCOL:
892eb268927Sgilles 		return "remote error in protocol";
893eb268927Sgilles 	case EX_NOPERM:
894eb268927Sgilles 		return "permission denied";
895eb268927Sgilles 	case EX_CONFIG:
896eb268927Sgilles 		return "local configuration error";
897eb268927Sgilles 	default:
898eb268927Sgilles 		break;
899eb268927Sgilles 	}
900eb268927Sgilles 	return NULL;
901eb268927Sgilles }
902eb268927Sgilles 
903