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