1 /* $Id$ */
2 
3 /*
4  * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <fcntl.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include "fdm.h"
26 #include "match.h"
27 
28 int	child_deliver(struct child *, struct io *);
29 
30 int
child_deliver(struct child * child,struct io * pio)31 child_deliver(struct child *child, struct io *pio)
32 {
33 	struct child_deliver_data	*data = child->data;
34 	struct account			*a = data->account;
35 	struct mail			*m = data->mail;
36 	struct msg			 msg;
37 	struct msgbuf			 msgbuf;
38 	int				 error = 0;
39 
40 	log_debug2("%s: deliver started, pid %ld", a->name, (long) getpid());
41 
42 #ifdef HAVE_SETPROCTITLE
43 	setproctitle("%s[%lu]", data->name, (u_long) geteuid());
44 #endif
45 
46 	/* Call the hook. */
47 	memset(&msg, 0, sizeof msg);
48 	data->hook(0, a, &msg, data, &msg.data.error);
49 
50 	/* Inform parent we're done. */
51 	msg.type = MSG_DONE;
52 	msg.id = 0;
53 
54 	msgbuf.buf = m->tags;
55 	msgbuf.len = STRB_SIZE(m->tags);
56 
57 	if (privsep_send(pio, &msg, &msgbuf) != 0)
58 		fatalx("privsep_send error");
59 	do {
60 		if (privsep_recv(pio, &msg, NULL) != 0)
61 			fatalx("privsep_recv error");
62 	} while (msg.type != MSG_EXIT);
63 
64 	return (error);
65 }
66 
67 void
child_deliver_action_hook(pid_t pid,struct account * a,struct msg * msg,struct child_deliver_data * data,int * result)68 child_deliver_action_hook(pid_t pid, struct account *a, struct msg *msg,
69     struct child_deliver_data *data, int *result)
70 {
71 	struct actitem		*ti = data->actitem;
72 	struct deliver_ctx	*dctx = data->dctx;
73 	struct mail		*m = data->mail;
74 	struct mail		*md = &dctx->wr_mail;
75 
76 	/* Check if this is the parent. */
77 	if (pid != 0) {
78 		/* Use new mail if necessary. */
79 		if (ti->deliver->type != DELIVER_WRBACK) {
80 			xfree(dctx);
81 			return;
82 		}
83 
84 		if (*result != DELIVER_SUCCESS)
85 			mail_destroy(md);
86 		else {
87 			mail_close(md);
88 			if (mail_receive(m, msg, 0) != 0) {
89 				log_warn("parent_deliver: can't receive mail");
90 				*result = DELIVER_FAILURE;
91 			}
92 		}
93 
94 		xfree(dctx);
95 		return;
96 	}
97 
98 	dctx->udata = xmalloc(sizeof *dctx->udata);
99 	dctx->udata->uid = data->uid;
100 	dctx->udata->gid =  data->gid;
101 	dctx->udata->name = xstrdup(find_tag(m->tags, "user"));
102 	dctx->udata->home = xstrdup(find_tag(m->tags, "home"));
103 	log_debug2("%s: deliver user is: %s (%lu/%lu), home is: %s", a->name,
104 	    dctx->udata->name, (u_long) dctx->udata->uid,
105 	    (u_long) dctx->udata->gid, dctx->udata->home);
106 
107 	/* This is the child. do the delivery. */
108 	*result = ti->deliver->deliver(dctx, ti);
109 	if (ti->deliver->type != DELIVER_WRBACK || *result != DELIVER_SUCCESS) {
110 		user_free(dctx->udata);
111 		return;
112 	}
113 	user_free(dctx->udata);
114 
115 	mail_send(md, msg);
116 	log_debug2("%s: using new mail, size %zu", a->name, md->size);
117 }
118 
119 void
child_deliver_cmd_hook(pid_t pid,struct account * a,unused struct msg * msg,struct child_deliver_data * data,int * result)120 child_deliver_cmd_hook(pid_t pid, struct account *a, unused struct msg *msg,
121     struct child_deliver_data *data, int *result)
122 {
123 	struct mail_ctx			*mctx = data->mctx;
124 	struct mail			*m = data->mail;
125 	struct match_command_data	*cmddata = data->cmddata;
126 	int				 flags, status, found = 0;
127 	char				*s, *cause, *lbuf, *out, *err, tag[24];
128 	size_t				 llen;
129 	struct cmd			*cmd = NULL;
130 	struct rmlist			 rml;
131 	u_int				 i;
132 
133 	/* If this is the parent, do nothing. */
134 	if (pid != 0) {
135 		xfree(mctx);
136 		return;
137 	}
138 
139 	/* Sort out the command. */
140 	s = replacepath(
141 	    &cmddata->cmd, m->tags, m, &m->rml, find_tag(m->tags, "home"));
142 	if (s == NULL || *s == '\0') {
143 		log_warnx("%s: empty command", a->name);
144 		goto error;
145 	}
146 
147 	log_debug2("%s: %s: started (ret=%d re=%s)", a->name, s, cmddata->ret,
148 	    cmddata->re.str == NULL ? "none" : cmddata->re.str);
149 	flags = CMD_ONCE;
150 	if (cmddata->pipe)
151 		flags |= CMD_IN;
152 	if (cmddata->re.str != NULL)
153 		flags |= CMD_OUT;
154 	cmd = cmd_start(s, flags, m->data, m->size, &cause);
155 	if (cmd == NULL) {
156 		log_warnx("%s: %s: %s", a->name, s, cause);
157 		goto error;
158 	}
159 
160 	llen = IO_LINESIZE;
161 	lbuf = xmalloc(llen);
162 
163 	for (;;) {
164 		/* Stop early if looking for regexp only. */
165 		if (found && cmddata->ret == -1) {
166 			log_debug3("%s: %s: found. stopping early", a->name, s);
167 			status = 1;
168 			break;
169 		}
170 
171 		status = cmd_poll(
172 		    cmd, &out, &err, &lbuf, &llen, conf.timeout, &cause);
173 		if (status == -1) {
174 			log_warnx("%s: %s: %s", a->name, s, cause);
175 			goto error;
176 		}
177 		if (status != 0)
178 			break;
179 		if (err != NULL)
180 			log_warnx("%s: %s: %s", a->name, s, err);
181 		if (out == NULL)
182 			continue;
183 		log_debug3("%s: %s: out: %s", a->name, s, out);
184 		if (found)
185 			continue;
186 
187 		found = re_string(&cmddata->re, out, &rml, &cause);
188 		if (found == -1) {
189 			log_warnx("%s: %s", a->name, cause);
190 			goto error;
191 		}
192 		if (found != 1)
193 			continue;
194 		/* Save the matches. */
195 		if (!rml.valid)
196 			continue;
197 		for (i = 0; i < NPMATCH; i++) {
198 			if (!rml.list[i].valid)
199 				break;
200 			xsnprintf(tag, sizeof tag, "command%u", i);
201 			add_tag(&m->tags, tag, "%.*s", (int) (rml.list[i].eo -
202 			    rml.list[i].so), out + rml.list[i].so);
203 		}
204 	}
205 	status--;
206 
207 	log_debug2("%s: %s: returned %d, found %d", a->name, s, status, found);
208 
209 	cmd_free(cmd);
210 	xfree(s);
211 	xfree(lbuf);
212 
213 	status = cmddata->ret == status;
214 	if (cmddata->ret != -1 && cmddata->re.str != NULL)
215 		*result = (found && status) ? MATCH_TRUE : MATCH_FALSE;
216 	else if (cmddata->ret != -1 && cmddata->re.str == NULL)
217 		*result = status ? MATCH_TRUE : MATCH_FALSE;
218 	else if (cmddata->ret == -1 && cmddata->re.str != NULL)
219 		*result = found ? MATCH_TRUE : MATCH_FALSE;
220 	else
221 		*result = MATCH_ERROR;
222 	return;
223 
224 error:
225 	if (cause != NULL)
226 		xfree(cause);
227 	if (cmd != NULL)
228 		cmd_free(cmd);
229 	if (s != NULL)
230 		xfree(s);
231 	if (lbuf != NULL)
232 		xfree(lbuf);
233 	*result = MATCH_ERROR;
234 }
235