/*++ /* 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 #include #include #include #include #include /* Utility library. */ #include #include #include #include #include #include /* Global library. */ #include #include #include #include #include #include #include #include #include /* 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); }