summaryrefslogtreecommitdiffstats
path: root/src/virtual/mailbox.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/virtual/mailbox.c283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/virtual/mailbox.c b/src/virtual/mailbox.c
new file mode 100644
index 0000000..19afca8
--- /dev/null
+++ b/src/virtual/mailbox.c
@@ -0,0 +1,283 @@
+/*++
+/* NAME
+/* mailbox 3
+/* SUMMARY
+/* mailbox delivery
+/* SYNOPSIS
+/* #include "virtual.h"
+/*
+/* int deliver_mailbox(state, usr_attr, statusp)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* int *statusp;
+/* DESCRIPTION
+/* deliver_mailbox() delivers to UNIX-style mailbox or to maildir.
+/*
+/* A zero result means that the named user was not found.
+/*
+/* Arguments:
+/* .IP state
+/* The attributes that specify the message, recipient and more.
+/* .IP usr_attr
+/* Attributes describing user rights and mailbox location.
+/* .IP statusp
+/* Delivery status: see below.
+/* DIAGNOSTICS
+/* The message delivery status is non-zero when delivery should be tried
+/* again.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <set_eugid.h>
+
+/* Global library. */
+
+#include <mail_copy.h>
+#include <mbox_open.h>
+#include <defer.h>
+#include <sent.h>
+#include <mail_params.h>
+#include <mail_addr_find.h>
+#include <dsn_util.h>
+
+/* Application-specific. */
+
+#include "virtual.h"
+
+#define YES 1
+#define NO 0
+
+/* deliver_mailbox_file - deliver to recipient mailbox */
+
+static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
+{
+ const char *myname = "deliver_mailbox_file";
+ DSN_BUF *why = state.msg_attr.why;
+ MBOX *mp;
+ int mail_copy_status;
+ int deliver_status;
+ int copy_flags;
+ struct stat st;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Don't deliver trace-only requests.
+ */
+ if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+ dsb_simple(why, "2.0.0", "delivers to mailbox");
+ return (sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * Initialize. Assume the operation will fail. Set the delivered
+ * attribute to reflect the final recipient.
+ */
+ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
+ msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
+ state.msg_attr.delivered = state.msg_attr.rcpt.address;
+ mail_copy_status = MAIL_COPY_STAT_WRITE;
+
+ /*
+ * Lock the mailbox and open/create the mailbox file.
+ *
+ * Write the file as the recipient, so that file quota work.
+ */
+ copy_flags = MAIL_COPY_MBOX;
+
+ set_eugid(usr_attr.uid, usr_attr.gid);
+ mp = mbox_open(usr_attr.mailbox, O_APPEND | O_WRONLY | O_CREAT,
+ S_IRUSR | S_IWUSR, &st, -1, -1,
+ virtual_mbox_lock_mask, "4.2.0", why);
+ if (mp != 0) {
+ if (S_ISREG(st.st_mode) == 0) {
+ vstream_fclose(mp->fp);
+ msg_warn("recipient %s: destination %s is not a regular file",
+ state.msg_attr.rcpt.address, usr_attr.mailbox);
+ dsb_simple(why, "5.3.5", "mail system configuration error");
+ } else if (var_strict_mbox_owner && st.st_uid != usr_attr.uid) {
+ vstream_fclose(mp->fp);
+ dsb_simple(why, "4.2.0",
+ "destination %s is not owned by recipient", usr_attr.mailbox);
+ msg_warn("specify \"%s = no\" to ignore mailbox ownership mismatch",
+ VAR_STRICT_MBOX_OWNER);
+ } else {
+ if (vstream_fseek(mp->fp, (off_t) 0, SEEK_END) < 0)
+ msg_fatal("%s: seek mailbox file %s: %m",
+ myname, VSTREAM_PATH(mp->fp));
+ mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
+ copy_flags, "\n", why);
+ }
+ mbox_release(mp);
+ }
+ set_eugid(var_owner_uid, var_owner_gid);
+
+ /*
+ * As the mail system, bounce, defer delivery, or report success.
+ */
+ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
+ deliver_status = DEL_STAT_DEFER;
+ } else if (mail_copy_status != 0) {
+ vstring_sprintf_prepend(why->reason, "delivery failed to mailbox %s: ",
+ usr_attr.mailbox);
+ deliver_status =
+ (STR(why->status)[0] == '4' ?
+ defer_append : bounce_append)
+ (BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+ dsb_simple(why, "2.0.0", "delivered to mailbox");
+ deliver_status = sent(BOUNCE_FLAGS(state.request),
+ SENT_ATTR(state.msg_attr));
+ }
+ return (deliver_status);
+}
+
+/* deliver_mailbox - deliver to recipient mailbox */
+
+int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
+{
+ const char *myname = "deliver_mailbox";
+ const char *mailbox_res;
+ const char *uid_res;
+ const char *gid_res;
+ DSN_BUF *why = state.msg_attr.why;
+ long n;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * Sanity check.
+ */
+ if (*var_virt_mailbox_base != '/')
+ msg_fatal("do not specify relative pathname: %s = %s",
+ VAR_VIRT_MAILBOX_BASE, var_virt_mailbox_base);
+
+ /*
+ * Look up the mailbox location. Bounce if not found, defer in case of
+ * trouble.
+ */
+#define IGNORE_EXTENSION ((char **) 0)
+
+ mailbox_res = mail_addr_find(virtual_mailbox_maps, state.msg_attr.user,
+ IGNORE_EXTENSION);
+ if (mailbox_res == 0) {
+ if (virtual_mailbox_maps->error == 0)
+ return (NO);
+ msg_warn("table %s: lookup %s: %m", virtual_mailbox_maps->title,
+ state.msg_attr.user);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ return (YES);
+ }
+ usr_attr.mailbox = concatenate(var_virt_mailbox_base, "/",
+ mailbox_res, (char *) 0);
+
+#define RETURN(res) { myfree(usr_attr.mailbox); return (res); }
+
+ /*
+ * Look up the mailbox owner rights. Defer in case of trouble.
+ */
+ uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user,
+ IGNORE_EXTENSION);
+ if (uid_res == 0) {
+ msg_warn("recipient %s: not found in %s",
+ state.msg_attr.user, virtual_uid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
+ }
+ if ((n = atol(uid_res)) < var_virt_minimum_uid) {
+ msg_warn("recipient %s: bad uid %s in %s",
+ state.msg_attr.user, uid_res, virtual_uid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
+ }
+ usr_attr.uid = (uid_t) n;
+
+ /*
+ * Look up the mailbox group rights. Defer in case of trouble.
+ */
+ gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user,
+ IGNORE_EXTENSION);
+ if (gid_res == 0) {
+ msg_warn("recipient %s: not found in %s",
+ state.msg_attr.user, virtual_gid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
+ }
+ if ((n = atol(gid_res)) <= 0) {
+ msg_warn("recipient %s: bad gid %s in %s",
+ state.msg_attr.user, gid_res, virtual_gid_maps->title);
+ dsb_simple(why, "4.3.5", "mail system configuration error");
+ *statusp = defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ RETURN(YES);
+ }
+ usr_attr.gid = (gid_t) n;
+
+ if (msg_verbose)
+ msg_info("%s[%d]: set user_attr: %s, uid = %u, gid = %u",
+ myname, state.level, usr_attr.mailbox,
+ (unsigned) usr_attr.uid, (unsigned) usr_attr.gid);
+
+ /*
+ * Deliver to mailbox or to maildir.
+ */
+#define LAST_CHAR(s) (s[strlen(s) - 1])
+
+ if (LAST_CHAR(usr_attr.mailbox) == '/')
+ *statusp = deliver_maildir(state, usr_attr);
+ else
+ *statusp = deliver_mailbox_file(state, usr_attr);
+
+ /*
+ * Cleanup.
+ */
+ RETURN(YES);
+}