diff options
Diffstat (limited to 'src/local/file.c')
-rw-r--r-- | src/local/file.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/local/file.c b/src/local/file.c new file mode 100644 index 0000000..0cc4c18 --- /dev/null +++ b/src/local/file.c @@ -0,0 +1,195 @@ +/*++ +/* NAME +/* file 3 +/* SUMMARY +/* mail delivery to arbitrary file +/* SYNOPSIS +/* #include "local.h" +/* +/* int deliver_file(state, usr_attr, path) +/* LOCAL_STATE state; +/* USER_ATTR usr_attr; +/* char *path; +/* DESCRIPTION +/* deliver_file() appends a message to a file, UNIX mailbox format, +/* or qmail maildir format, +/* with duplicate suppression. It will deliver only to non-executable +/* regular files. +/* +/* Arguments: +/* .IP state +/* The attributes that specify the message, recipient and more. +/* Attributes describing alias, include or forward expansion. +/* A table with the results from expanding aliases or lists. +/* .IP usr_attr +/* Attributes describing user rights and environment information. +/* .IP path +/* The file to deliver to. If the name ends in '/', delivery is done +/* in qmail maildir format, otherwise delivery is done in UNIX mailbox +/* format. +/* DIAGNOSTICS +/* deliver_file() returns non-zero when delivery should be tried again. +/* SEE ALSO +/* defer(3) +/* bounce(3) +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <htable.h> +#include <vstring.h> +#include <vstream.h> +#include <deliver_flock.h> +#include <set_eugid.h> + +/* Global library. */ + +#include <mail_copy.h> +#include <bounce.h> +#include <defer.h> +#include <sent.h> +#include <been_here.h> +#include <mail_params.h> +#include <mbox_conf.h> +#include <mbox_open.h> +#include <dsn_util.h> + +/* Application-specific. */ + +#include "local.h" + +/* deliver_file - deliver to file */ + +int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) +{ + const char *myname = "deliver_file"; + struct stat st; + MBOX *mp; + DSN_BUF *why = state.msg_attr.why; + int mail_copy_status = MAIL_COPY_STAT_WRITE; + int deliver_status; + int copy_flags; + + /* + * Make verbose logging easier to understand. + */ + state.level++; + if (msg_verbose) + MSG_LOG_STATE(myname, state); + + /* + * DUPLICATE ELIMINATION + * + * Skip this file if it was already delivered to as this user. + */ + if (been_here(state.dup_filter, "file %ld %s", (long) usr_attr.uid, path)) + return (0); + + /* + * DELIVERY POLICY + * + * Do we allow delivery to files? + */ + if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) { + dsb_simple(why, "5.7.1", "mail to file is restricted"); + /* Account for possible owner- sender address override. */ + return (bounce_workaround(state)); + } + + /* + * Don't deliver trace-only requests. + */ + if (DEL_REQ_TRACE_ONLY(state.request->flags)) { + dsb_simple(why, "2.0.0", "delivers to file: %s", path); + return (sent(BOUNCE_FLAGS(state.request), + SENT_ATTR(state.msg_attr))); + } + + /* + * DELIVERY RIGHTS + * + * Use a default uid/gid when none are given. + */ + if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) + msg_panic("privileged default user id"); + if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) + msg_panic("privileged default group id"); + + /* + * If the name ends in /, use maildir-style delivery instead. + */ + if (path[strlen(path) - 1] == '/') + return (deliver_maildir(state, usr_attr, path)); + + /* + * Deliver. From here on, no early returns or we have a memory leak. + */ + if (msg_verbose) + msg_info("deliver_file (%ld,%ld): %s", + (long) usr_attr.uid, (long) usr_attr.gid, path); + if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) + msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id); + + /* + * As the specified user, open or create the file, lock it, and append + * the message. + */ + copy_flags = MAIL_COPY_MBOX; + if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0) + copy_flags &= ~MAIL_COPY_DELIVERED; + + set_eugid(usr_attr.uid, usr_attr.gid); + mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR, &st, -1, -1, + local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL, + "5.2.0", why); + if (mp != 0) { + if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + vstream_fclose(mp->fp); + dsb_simple(why, "5.7.1", "file is executable"); + } else { + mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp, + S_ISREG(st.st_mode) ? copy_flags : + (copy_flags & ~MAIL_COPY_TOFILE), + "\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, + "cannot append message to file %s: ", path); + /* Account for possible owner- sender address override. */ + deliver_status = bounce_workaround(state); + } else { + dsb_simple(why, "2.0.0", "delivered to file: %s", path); + deliver_status = sent(BOUNCE_FLAGS(state.request), + SENT_ATTR(state.msg_attr)); + } + return (deliver_status); +} |