1/*++ 2/* NAME 3/* file 3 4/* SUMMARY 5/* mail delivery to arbitrary file 6/* SYNOPSIS 7/* #include "local.h" 8/* 9/* int deliver_file(state, usr_attr, path) 10/* LOCAL_STATE state; 11/* USER_ATTR usr_attr; 12/* char *path; 13/* DESCRIPTION 14/* deliver_file() appends a message to a file, UNIX mailbox format, 15/* or qmail maildir format, 16/* with duplicate suppression. It will deliver only to non-executable 17/* regular files. 18/* 19/* Arguments: 20/* .IP state 21/* The attributes that specify the message, recipient and more. 22/* Attributes describing alias, include or forward expansion. 23/* A table with the results from expanding aliases or lists. 24/* .IP usr_attr 25/* Attributes describing user rights and environment information. 26/* .IP path 27/* The file to deliver to. If the name ends in '/', delivery is done 28/* in qmail maildir format, otherwise delivery is done in UNIX mailbox 29/* format. 30/* DIAGNOSTICS 31/* deliver_file() returns non-zero when delivery should be tried again. 32/* SEE ALSO 33/* defer(3) 34/* bounce(3) 35/* LICENSE 36/* .ad 37/* .fi 38/* The Secure Mailer license must be distributed with this software. 39/* AUTHOR(S) 40/* Wietse Venema 41/* IBM T.J. Watson Research 42/* P.O. Box 704 43/* Yorktown Heights, NY 10598, USA 44/*--*/ 45 46/* System library. */ 47 48#include <sys_defs.h> 49#include <sys/stat.h> 50#include <unistd.h> 51#include <fcntl.h> 52#include <errno.h> 53#include <string.h> 54 55/* Utility library. */ 56 57#include <msg.h> 58#include <htable.h> 59#include <vstring.h> 60#include <vstream.h> 61#include <deliver_flock.h> 62#include <set_eugid.h> 63 64/* Global library. */ 65 66#include <mail_copy.h> 67#include <bounce.h> 68#include <defer.h> 69#include <sent.h> 70#include <been_here.h> 71#include <mail_params.h> 72#include <mbox_conf.h> 73#include <mbox_open.h> 74#include <dsn_util.h> 75 76/* Application-specific. */ 77 78#include "local.h" 79 80/* deliver_file - deliver to file */ 81 82int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) 83{ 84 const char *myname = "deliver_file"; 85 struct stat st; 86 MBOX *mp; 87 DSN_BUF *why = state.msg_attr.why; 88 int mail_copy_status = MAIL_COPY_STAT_WRITE; 89 int deliver_status; 90 int copy_flags; 91 92 /* 93 * Make verbose logging easier to understand. 94 */ 95 state.level++; 96 if (msg_verbose) 97 MSG_LOG_STATE(myname, state); 98 99 /* 100 * DUPLICATE ELIMINATION 101 * 102 * Skip this file if it was already delivered to as this user. 103 */ 104 if (been_here(state.dup_filter, "file %ld %s", (long) usr_attr.uid, path)) 105 return (0); 106 107 /* 108 * DELIVERY POLICY 109 * 110 * Do we allow delivery to files? 111 */ 112 if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) { 113 dsb_simple(why, "5.7.1", "mail to file is restricted"); 114 /* Account for possible owner- sender address override. */ 115 return (bounce_workaround(state)); 116 } 117 118 /* 119 * Don't deliver trace-only requests. 120 */ 121 if (DEL_REQ_TRACE_ONLY(state.request->flags)) { 122 dsb_simple(why, "2.0.0", "delivers to file: %s", path); 123 return (sent(BOUNCE_FLAGS(state.request), 124 SENT_ATTR(state.msg_attr))); 125 } 126 127 /* 128 * DELIVERY RIGHTS 129 * 130 * Use a default uid/gid when none are given. 131 */ 132 if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0) 133 msg_panic("privileged default user id"); 134 if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0) 135 msg_panic("privileged default group id"); 136 137 /* 138 * If the name ends in /, use maildir-style delivery instead. 139 */ 140 if (path[strlen(path) - 1] == '/') 141 return (deliver_maildir(state, usr_attr, path)); 142 143 /* 144 * Deliver. From here on, no early returns or we have a memory leak. 145 */ 146 if (msg_verbose) 147 msg_info("deliver_file (%ld,%ld): %s", 148 (long) usr_attr.uid, (long) usr_attr.gid, path); 149 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) 150 msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id); 151 152 /* 153 * As the specified user, open or create the file, lock it, and append 154 * the message. 155 */ 156 copy_flags = MAIL_COPY_MBOX; 157 if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0) 158 copy_flags &= ~MAIL_COPY_DELIVERED; 159 160 set_eugid(usr_attr.uid, usr_attr.gid); 161 mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY, 162 S_IRUSR | S_IWUSR, &st, -1, -1, 163 local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL, 164 "5.2.0", why); 165 if (mp != 0) { 166 if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 167 vstream_fclose(mp->fp); 168 dsb_simple(why, "5.7.1", "file is executable"); 169 } else { 170 mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp, 171 S_ISREG(st.st_mode) ? copy_flags : 172 (copy_flags & ~MAIL_COPY_TOFILE), 173 "\n", why); 174 } 175 mbox_release(mp); 176 } 177 set_eugid(var_owner_uid, var_owner_gid); 178 179 /* 180 * As the mail system, bounce, defer delivery, or report success. 181 */ 182 if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { 183 deliver_status = DEL_STAT_DEFER; 184 } else if (mail_copy_status != 0) { 185 vstring_sprintf_prepend(why->reason, 186 "cannot append message to file %s: ", path); 187 if (STR(why->status)[0] == '4') 188 deliver_status = 189 defer_append(BOUNCE_FLAGS(state.request), 190 BOUNCE_ATTR(state.msg_attr)); 191 else 192 /* Account for possible owner- sender address override. */ 193 deliver_status = bounce_workaround(state); 194 } else { 195 dsb_simple(why, "2.0.0", "delivered to file: %s", path); 196 deliver_status = sent(BOUNCE_FLAGS(state.request), 197 SENT_ATTR(state.msg_attr)); 198 } 199 return (deliver_status); 200} 201