1 /* deliver.c -- deliver shell - just calls lmtpd
2 * Tim Martin
3 *
4 * Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 *
18 * 3. The name "Carnegie Mellon University" must not be used to
19 * endorse or promote products derived from this software without
20 * prior written permission. For permission or any legal
21 * details, please contact
22 * Carnegie Mellon University
23 * Center for Technology Transfer and Enterprise Creation
24 * 4615 Forbes Avenue
25 * Suite 302
26 * Pittsburgh, PA 15213
27 * (412) 268-7393, fax: (412) 268-7395
28 * innovation@andrew.cmu.edu
29 *
30 * 4. Redistributions of any form whatsoever must retain the following
31 * acknowledgment:
32 * "This product includes software developed by Computing Services
33 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34 *
35 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
36 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
38 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
41 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42 */
43
44 #include <config.h>
45
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sysexits.h>
54 #include <fcntl.h>
55 #include <sys/stat.h>
56 #include <errno.h>
57 #include <pwd.h>
58 #include <sys/types.h>
59
60 #include <netdb.h>
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <sys/un.h>
65
66 #include "global.h"
67 #include "xmalloc.h"
68 #include "xstrlcpy.h"
69 #include "xstrlcat.h"
70 #include "lmtpengine.h"
71 #include "prot.h"
72 #include "proxy.h"
73 #include "version.h"
74
75 /* generated headers are not necessarily in current directory */
76 #include "imap/imap_err.h"
77
78 extern int optind;
79 extern char *optarg;
80
81 static int logdebug = 0;
82
83 static struct protstream *deliver_out, *deliver_in;
84
85 static const char *sockaddr;
86
87 static struct protocol_t lmtp_protocol =
88 { "lmtp", "lmtp", TYPE_STD,
89 { { { 0, "220 " },
90 { "LHLO", "deliver", "250 ", NULL,
91 CAPAF_ONE_PER_LINE|CAPAF_SKIP_FIRST_WORD|CAPAF_DASH_STUFFING,
92 { { "AUTH", CAPA_AUTH },
93 { "STARTTLS", CAPA_STARTTLS },
94 { "PIPELINING", CAPA_PIPELINING },
95 { "IGNOREQUOTA", CAPA_IGNOREQUOTA },
96 { NULL, 0 } } },
97 { "STARTTLS", "220", "454", 0 },
98 { "AUTH", 512, 0, "235", "5", "334 ", "*", NULL, 0 },
99 { NULL, NULL, NULL },
100 { "NOOP", NULL, "250" },
101 { "QUIT", NULL, "221" } } }
102 };
103
104 /* unused for deliver.c, but needed to make lmtpengine.c happy */
105 int deliver_logfd = -1;
106
107 /* forward declarations */
108
109 static int deliver_msg(char *return_path, char *authuser, int ignorequota,
110 char **users, int numusers, char *mailbox);
111 static struct backend *init_net(const char *sockaddr);
112
usage(void)113 static void usage(void)
114 {
115 fprintf(stderr,
116 "421-4.3.0 usage: deliver [-C <alt_config> ] [-m mailbox]"
117 " [-a auth] [-r return_path] [-l] [-D]\r\n");
118 fprintf(stderr, "421 4.3.0 %s\n", CYRUS_VERSION);
119 exit(EX_USAGE);
120 }
121
fatal(const char * s,int code)122 EXPORTED void fatal(const char* s, int code)
123 {
124 static int recurse_code = 0;
125
126 if(recurse_code) exit(code);
127 else recurse_code = 0;
128
129 prot_printf(deliver_out,"421 4.3.0 deliver: %s\r\n", s);
130 prot_flush(deliver_out);
131 cyrus_done();
132 exit(code);
133 }
134
135 /*
136 * Here we're just an intermediatory piping stdin to lmtp socket
137 * and lmtp socket to stdout
138 */
pipe_through(struct backend * conn)139 void pipe_through(struct backend *conn)
140 {
141 struct protgroup *protin = protgroup_new(2);
142
143 protgroup_insert(protin, deliver_in);
144 protgroup_insert(protin, conn->in);
145
146 do {
147 /* Flush any buffered output */
148 prot_flush(deliver_out);
149 prot_flush(conn->out);
150
151 } while (!proxy_check_input(protin, deliver_in, deliver_out,
152 conn->in, conn->out, 0));
153
154 /* ok, we're done. */
155 protgroup_free(protin);
156
157 return;
158 }
159
main(int argc,char ** argv)160 int main(int argc, char **argv)
161 {
162 int r = 0;
163 int opt;
164 int lmtpflag = 0;
165 int ignorequota = 0;
166 char *mailboxname = NULL;
167 char *authuser = NULL;
168 char *return_path = NULL;
169 char buf[1024];
170 char *alt_config = NULL;
171
172 while ((opt = getopt(argc, argv, "C:df:r:m:a:F:eE:lqD")) != EOF) {
173 switch(opt) {
174 case 'C': /* alt config file */
175 alt_config = optarg;
176 break;
177
178 case 'd':
179 /* Ignore -- /bin/mail compatibility flags */
180 break;
181
182 case 'D':
183 logdebug = 1;
184 break;
185
186 case 'r':
187 case 'f':
188 return_path = optarg;
189 break;
190
191 case 'm':
192 if (mailboxname) {
193 fprintf(stderr, "deliver: multiple -m options\n");
194 usage();
195 /* NOTREACHED */
196 }
197 if (*optarg) mailboxname = optarg;
198 break;
199
200 case 'a':
201 if (authuser) {
202 fprintf(stderr, "deliver: multiple -a options\n");
203 usage();
204 /* NOTREACHED */
205 }
206 authuser = optarg;
207 break;
208
209 case 'F': /* set IMAP flag. we no longer support this */
210 fprintf(stderr,"deliver: 'F' option no longer supported\n");
211 usage();
212 break;
213
214 case 'e':
215 /* duplicate delivery. ignore */
216 break;
217
218 case 'E':
219 fprintf(stderr,"deliver: 'E' option no longer supported\n");
220 usage();
221 break;
222
223 case 'l':
224 lmtpflag = 1;
225 break;
226
227 case 'q':
228 ignorequota = 1;
229 break;
230
231 default:
232 usage();
233 /* NOTREACHED */
234 }
235 }
236
237 deliver_in = prot_new(0, 0);
238 deliver_out = prot_new(1, 1);
239 prot_setflushonread(deliver_in, deliver_out);
240 prot_settimeout(deliver_in, 300);
241
242 cyrus_init(alt_config, "deliver", CYRUSINIT_NODB, CONFIG_NEED_PARTITION_DATA);
243
244 sockaddr = config_getstring(IMAPOPT_LMTPSOCKET);
245 if (!sockaddr) {
246 strlcpy(buf, config_dir, sizeof(buf));
247 strlcat(buf, "/socket/lmtp", sizeof(buf));
248 sockaddr = buf;
249 }
250
251 if (lmtpflag == 1) {
252 struct backend *conn = init_net(sockaddr);
253
254 pipe_through(conn);
255
256 backend_disconnect(conn);
257 free(conn);
258 }
259 else {
260 if (return_path == NULL) {
261 uid_t me = getuid();
262 struct passwd *p = getpwuid(me);
263 return_path = p->pw_name;
264 }
265
266 /* deliver to users or global mailbox */
267 r = deliver_msg(return_path,authuser, ignorequota,
268 argv+optind, argc - optind, mailboxname);
269 }
270
271 cyrus_done();
272
273 return r;
274 }
275
just_exit(const char * msg)276 static void just_exit(const char *msg)
277 {
278 com_err(msg, 0, "%s", error_message(errno));
279
280 fatal(msg, EX_CONFIG);
281 }
282
283 /* initialize the network
284 * we talk on unix sockets
285 */
init_net(const char * unixpath)286 static struct backend *init_net(const char *unixpath)
287 {
288 int lmtpdsock;
289 struct sockaddr_un addr;
290 struct backend *conn;
291
292 if ((lmtpdsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
293 just_exit("socket failed");
294 }
295
296 addr.sun_family = AF_UNIX;
297 strlcpy(addr.sun_path, unixpath, sizeof(addr.sun_path));
298
299 if (connect(lmtpdsock, (struct sockaddr *) &addr,
300 sizeof(addr.sun_family) + strlen(addr.sun_path) + 1) < 0) {
301 just_exit("connect failed");
302 }
303
304 conn = xzmalloc(sizeof(struct backend));
305 conn->timeout = NULL;
306 conn->in = prot_new(lmtpdsock, 0);
307 conn->out = prot_new(lmtpdsock, 1);
308 conn->sock = lmtpdsock;
309 prot_setflushonread(conn->in, conn->out);
310 conn->prot = &lmtp_protocol;
311
312 return conn;
313 }
314
deliver_msg(char * return_path,char * authuser,int ignorequota,char ** users,int numusers,char * mailbox)315 static int deliver_msg(char *return_path, char *authuser, int ignorequota,
316 char **users, int numusers, char *mailbox)
317 {
318 int r;
319 struct backend *conn;
320 struct lmtp_txn *txn = LMTP_TXN_ALLOC(numusers ? numusers : 1);
321 int j;
322 int ml = 0;
323
324 /* must have either some users or a mailbox */
325 if (!numusers && !mailbox) {
326 usage();
327 }
328
329 /* connect */
330 conn = backend_connect(NULL, sockaddr, &lmtp_protocol,
331 "", NULL, NULL, -1);
332 if (!conn) {
333 just_exit("couldn't connect to lmtpd");
334 }
335
336 /* setup txn */
337 txn->from = return_path;
338 txn->auth = authuser;
339 txn->data = deliver_in;
340 txn->isdotstuffed = 0;
341 txn->tempfail_unknown_mailbox = 0;
342 txn->rcpt_num = numusers ? numusers : 1;
343 if (mailbox) ml = strlen(mailbox);
344 if (numusers == 0) {
345 /* just deliver to mailbox 'mailbox' */
346 const char *BB = config_getstring(IMAPOPT_POSTUSER);
347 txn->rcpt[0].addr = (char *) xmalloc(ml + strlen(BB) + 2); /* xxx leaks! */
348 sprintf(txn->rcpt[0].addr, "%s+%s", BB, mailbox);
349 txn->rcpt[0].ignorequota = ignorequota;
350 } else {
351 /* setup each recipient */
352 for (j = 0; j < numusers; j++) {
353 if (mailbox) {
354 size_t ulen;
355
356 txn->rcpt[j].addr =
357 (char *) xmalloc(strlen(users[j]) + ml + 2);
358
359 /* find the length of the userid minus the domain */
360 ulen = strcspn(users[j], "@");
361 sprintf(txn->rcpt[j].addr, "%.*s+%s",
362 (int) ulen, users[j], mailbox);
363
364 /* add the domain if we have one */
365 if (ulen < strlen(users[j]))
366 strcat(txn->rcpt[j].addr, users[j]+ulen);
367 } else {
368 txn->rcpt[j].addr = xstrdup(users[j]);
369 }
370 txn->rcpt[j].ignorequota = ignorequota;
371 }
372 }
373
374 /* run txn */
375 r = lmtp_runtxn(conn, txn);
376
377 /* disconnect */
378 backend_disconnect(conn);
379 free(conn);
380
381 /* examine txn for error state */
382 r = 0;
383 for (j = 0; j < txn->rcpt_num; j++) {
384 switch (txn->rcpt[j].result) {
385 case RCPT_GOOD:
386 break;
387
388 case RCPT_TEMPFAIL:
389 r = EX_TEMPFAIL;
390 break;
391
392 case RCPT_PERMFAIL:
393 /* we just need any permanent failure, though we should
394 probably return data from the client-side LMTP info */
395 printf("%s: %s\n",
396 txn->rcpt[j].addr, error_message(txn->rcpt[j].r));
397 if (r != EX_TEMPFAIL) {
398 r = EX_DATAERR;
399 }
400 break;
401 }
402 free(txn->rcpt[j].addr);
403 strarray_free(txn->rcpt[j].resp);
404 }
405
406 free(txn);
407
408 /* return appropriately */
409 return r;
410 }
411