/* deliver.c -- deliver shell - just calls lmtpd * Tim Martin * * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* $Id: deliver.c,v 1.174 2004/05/22 03:45:48 rjs3 Exp $ */ #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "exitcodes.h" #include "imap_err.h" #include "xmalloc.h" #include "lmtpengine.h" #include "prot.h" #include "version.h" /* config.c stuff */ const int config_need_data = CONFIG_NEED_PARTITION_DATA; extern int optind; extern char *optarg; static int lmtpdsock; static int logdebug = 0; static struct protstream *deliver_out, *deliver_in; static const char *sockaddr; /* unused for deliver.c, but needed to make lmtpengine.c happy */ int deliver_logfd = -1; /* forward declarations */ static int deliver_msg(char *return_path, char *authuser, int ignorequota, char **users, int numusers, char *mailbox); static int init_net(const char *sockaddr); static void usage() { fprintf(stderr, "421-4.3.0 usage: deliver [-C ] [-m mailbox]" " [-a auth] [-r return_path] [-l] [-D]\r\n"); fprintf(stderr, "421 4.3.0 %s\n", CYRUS_VERSION); exit(EC_USAGE); } void fatal(const char* s, int code) { static int recurse_code = 0; if(recurse_code) exit(code); else recurse_code = 0; prot_printf(deliver_out,"421 4.3.0 deliver: %s\r\n", s); prot_flush(deliver_out); cyrus_done(); exit(code); } static int push(int in, int out) { char buf[4096]; int len; int cnt; int amnt = 0; len = read(in, buf, sizeof(buf)-1); if (len == -1) { exit(EC_IOERR); } if (len == 0) { exit(0); } /* keep writing until we have written the whole thing xxx can cause deadlock??? */ do { cnt = write(out, buf+amnt,len-amnt); if (cnt == -1) exit(EC_IOERR); amnt += cnt; } while (amnt= highest) highest = lmtp_in+1; FD_SET(local_in, &read_set); FD_ZERO(&write_set); FD_SET(lmtp_out, &write_set); if (lmtp_out >= highest) highest = lmtp_out+1; FD_SET(local_out, &write_set); while (1) { rset = read_set; wset = write_set; nfound = select(highest, &rset, &wset, NULL, NULL); if (nfound < 0) { if (errno == EINTR) continue; exit(EC_IOERR); } if ((FD_ISSET(lmtp_in,&rset)) && (FD_ISSET(local_out,&wset))) { push(lmtp_in, local_out); } if ((FD_ISSET(local_in,&rset)) && (FD_ISSET(lmtp_out,&wset))) { push(local_in, lmtp_out); } else { /* weird; this shouldn't happen */ usleep(1000); } } } int main(int argc, char **argv) { int r; int opt; int lmtpflag = 0; int ignorequota = 0; char *mailboxname = NULL; char *authuser = NULL; char *return_path = NULL; char buf[1024]; char *alt_config = NULL; while ((opt = getopt(argc, argv, "C:df:r:m:a:F:eE:lqD")) != EOF) { switch(opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 'd': /* Ignore -- /bin/mail compatibility flags */ break; case 'D': logdebug = 1; break; case 'r': case 'f': return_path = optarg; break; case 'm': if (mailboxname) { fprintf(stderr, "deliver: multiple -m options\n"); usage(); /* NOTREACHED */ } if (*optarg) mailboxname = optarg; break; case 'a': if (authuser) { fprintf(stderr, "deliver: multiple -a options\n"); usage(); /* NOTREACHED */ } authuser = optarg; break; case 'F': /* set IMAP flag. we no longer support this */ fprintf(stderr,"deliver: 'F' option no longer supported\n"); usage(); break; case 'e': /* duplicate delivery. ignore */ break; case 'E': fprintf(stderr,"deliver: 'E' option no longer supported\n"); usage(); break; case 'l': lmtpflag = 1; break; case 'q': ignorequota = 1; break; default: usage(); /* NOTREACHED */ } } deliver_in = prot_new(0, 0); deliver_out = prot_new(1, 1); prot_setflushonread(deliver_in, deliver_out); prot_settimeout(deliver_in, 300); cyrus_init(alt_config, "deliver", CYRUSINIT_NODB); sockaddr = config_getstring(IMAPOPT_LMTPSOCKET); if (!sockaddr) { strlcpy(buf, config_dir, sizeof(buf)); strlcat(buf, "/socket/lmtp", sizeof(buf)); sockaddr = buf; } if (lmtpflag == 1) { int s = init_net(sockaddr); pipe_through(s,s,0,1); } if (return_path == NULL) { uid_t me = getuid(); struct passwd *p = getpwuid(me); return_path = p->pw_name; } /* deliver to users or global mailbox */ r = deliver_msg(return_path,authuser, ignorequota, argv+optind, argc - optind, mailboxname); cyrus_done(); return r; } void just_exit(const char *msg) { com_err(msg, 0, error_message(errno)); fatal(msg, EC_CONFIG); } /* initialize the network * we talk on unix sockets */ static int init_net(const char *unixpath) { struct sockaddr_un addr; if ((lmtpdsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { just_exit("socket failed"); } addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, unixpath, sizeof(addr.sun_path)); if (connect(lmtpdsock, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path) + 1) < 0) { just_exit("connect failed"); } return lmtpdsock; } static int deliver_msg(char *return_path, char *authuser, int ignorequota, char **users, int numusers, char *mailbox) { int r; struct backend *conn; struct lmtp_txn *txn = LMTP_TXN_ALLOC(numusers ? numusers : 1); int j; int ml = 0; /* must have either some users or a mailbox */ if (!numusers && !mailbox) { usage(); } /* connect */ conn = backend_connect(NULL, sockaddr, &protocol[PROTOCOL_LMTP], "", NULL); if (!conn) { just_exit("couldn't connect to lmtpd"); } /* setup txn */ txn->from = return_path; txn->auth = authuser; txn->data = deliver_in; txn->isdotstuffed = 0; txn->tempfail_unknown_mailbox = 0; txn->rcpt_num = numusers ? numusers : 1; if (mailbox) ml = strlen(mailbox); if (numusers == 0) { /* just deliver to mailbox 'mailbox' */ const char *BB = config_getstring(IMAPOPT_POSTUSER); txn->rcpt[0].addr = (char *) xmalloc(ml + strlen(BB) + 2); /* xxx leaks! */ sprintf(txn->rcpt[0].addr, "%s+%s", BB, mailbox); txn->rcpt[0].ignorequota = ignorequota; } else { /* setup each recipient */ for (j = 0; j < numusers; j++) { if (mailbox) { size_t ulen; /* we let it leak ! */ txn->rcpt[j].addr = (char *) xmalloc(strlen(users[j]) + ml + 2); /* find the length of the userid minus the domain */ ulen = strcspn(users[j], "@"); sprintf(txn->rcpt[j].addr, "%.*s+%s", ulen, users[j], mailbox); /* add the domain if we have one */ if (ulen < strlen(users[j])) strcat(txn->rcpt[j].addr, users[j]+ulen); } else { txn->rcpt[j].addr = users[j]; } txn->rcpt[j].ignorequota = ignorequota; } } /* run txn */ r = lmtp_runtxn(conn, txn); /* disconnect */ backend_disconnect(conn, &protocol[PROTOCOL_LMTP]); /* examine txn for error state */ r = 0; for (j = 0; j < txn->rcpt_num; j++) { switch (txn->rcpt[j].result) { case RCPT_GOOD: break; case RCPT_TEMPFAIL: r = EC_TEMPFAIL; break; case RCPT_PERMFAIL: /* we just need any permanent failure, though we should probably return data from the client-side LMTP info */ printf("%s: %s\n", txn->rcpt[j].addr, error_message(txn->rcpt[j].r)); if (r != EC_TEMPFAIL) { r = EC_DATAERR; } break; } } /* return appropriately */ return r; }