1 /* $NetBSD: maildir.c,v 1.1.1.2 2013/01/02 18:59:01 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* maildir 3 6 /* SUMMARY 7 /* delivery to maildir 8 /* SYNOPSIS 9 /* #include "local.h" 10 /* 11 /* int deliver_maildir(state, usr_attr, path) 12 /* LOCAL_STATE state; 13 /* USER_ATTR usr_attr; 14 /* char *path; 15 /* DESCRIPTION 16 /* deliver_maildir() delivers a message to a qmail maildir. 17 /* 18 /* Arguments: 19 /* .IP state 20 /* The attributes that specify the message, recipient and more. 21 /* Attributes describing alias, include or forward expansion. 22 /* A table with the results from expanding aliases or lists. 23 /* .IP usr_attr 24 /* Attributes describing user rights and environment information. 25 /* .IP path 26 /* The maildir to deliver to, including trailing slash. 27 /* DIAGNOSTICS 28 /* deliver_maildir() always succeeds or it bounces the message. 29 /* SEE ALSO 30 /* bounce(3) 31 /* LICENSE 32 /* .ad 33 /* .fi 34 /* The Secure Mailer license must be distributed with this software. 35 /* AUTHOR(S) 36 /* Wietse Venema 37 /* IBM T.J. Watson Research 38 /* P.O. Box 704 39 /* Yorktown Heights, NY 10598, USA 40 /*--*/ 41 42 /* System library. */ 43 44 #include "sys_defs.h" 45 #include <sys/stat.h> 46 #include <sys/time.h> 47 #include <unistd.h> 48 #include <time.h> 49 #include <errno.h> 50 51 /* Utility library. */ 52 53 #include <msg.h> 54 #include <mymalloc.h> 55 #include <stringops.h> 56 #include <vstream.h> 57 #include <vstring.h> 58 #include <make_dirs.h> 59 #include <set_eugid.h> 60 #include <get_hostname.h> 61 #include <sane_fsops.h> 62 #include <warn_stat.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 <mail_params.h> 71 #include <dsn_util.h> 72 #include <mbox_open.h> 73 74 /* Application-specific. */ 75 76 #include "local.h" 77 78 /* deliver_maildir - delivery to maildir-style mailbox */ 79 80 int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path) 81 { 82 const char *myname = "deliver_maildir"; 83 char *newdir; 84 char *tmpdir; 85 char *curdir; 86 char *tmpfile; 87 char *newfile; 88 DSN_BUF *why = state.msg_attr.why; 89 VSTRING *buf; 90 VSTREAM *dst; 91 int mail_copy_status; 92 int deliver_status; 93 int copy_flags; 94 struct stat st; 95 struct timeval starttime; 96 97 GETTIMEOFDAY(&starttime); 98 99 /* 100 * Make verbose logging easier to understand. 101 */ 102 state.level++; 103 if (msg_verbose) 104 MSG_LOG_STATE(myname, state); 105 106 /* 107 * Don't deliver trace-only requests. 108 */ 109 if (DEL_REQ_TRACE_ONLY(state.request->flags)) { 110 dsb_simple(why, "2.0.0", "delivers to maildir"); 111 return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr))); 112 } 113 114 /* 115 * Initialize. Assume the operation will fail. Set the delivered 116 * attribute to reflect the final recipient. 117 */ 118 if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) 119 msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); 120 if (var_frozen_delivered == 0) 121 state.msg_attr.delivered = state.msg_attr.rcpt.address; 122 mail_copy_status = MAIL_COPY_STAT_WRITE; 123 buf = vstring_alloc(100); 124 125 copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_ORIG_RCPT; 126 if (local_deliver_hdr_mask & DELIVER_HDR_FILE) 127 copy_flags |= MAIL_COPY_DELIVERED; 128 129 newdir = concatenate(path, "new/", (char *) 0); 130 tmpdir = concatenate(path, "tmp/", (char *) 0); 131 curdir = concatenate(path, "cur/", (char *) 0); 132 133 /* 134 * Create and write the file as the recipient, so that file quota work. 135 * Create any missing directories on the fly. The file name is chosen 136 * according to ftp://koobera.math.uic.edu/www/proto/maildir.html: 137 * 138 * "A unique name has three pieces, separated by dots. On the left is the 139 * result of time(). On the right is the result of gethostname(). In the 140 * middle is something that doesn't repeat within one second on a single 141 * host. I fork a new process for each delivery, so I just use the 142 * process ID. If you're delivering several messages from one process, 143 * use starttime.pid_count.host, where starttime is the time that your 144 * process started, and count is the number of messages you've 145 * delivered." 146 * 147 * Well, that stopped working on fast machines, and on operating systems 148 * that randomize process ID values. When creating a file in tmp/ we use 149 * the process ID because it still is an exclusive resource. When moving 150 * the file to new/ we use the device number and inode number. I do not 151 * care if this breaks on a remote AFS file system, because people should 152 * know better. 153 * 154 * On January 26, 2003, http://cr.yp.to/proto/maildir.html said: 155 * 156 * A unique name has three pieces, separated by dots. On the left is the 157 * result of time() or the second counter from gettimeofday(). On the 158 * right is the result of gethostname(). (To deal with invalid host 159 * names, replace / with \057 and : with \072.) In the middle is a 160 * delivery identifier, discussed below. 161 * 162 * [...] 163 * 164 * Modern delivery identifiers are created by concatenating enough of the 165 * following strings to guarantee uniqueness: 166 * 167 * [...] 168 * 169 * In, where n is (in hexadecimal) the UNIX inode number of this file. 170 * Unfortunately, inode numbers aren't always available through NFS. 171 * 172 * Vn, where n is (in hexadecimal) the UNIX device number of this file. 173 * Unfortunately, device numbers aren't always available through NFS. 174 * (Device numbers are also not helpful with the standard UNIX 175 * filesystem: a maildir has to be within a single UNIX device for link() 176 * and rename() to work.) 177 * 178 * Mn, where n is (in decimal) the microsecond counter from the same 179 * gettimeofday() used for the left part of the unique name. 180 * 181 * Pn, where n is (in decimal) the process ID. 182 * 183 * [...] 184 */ 185 set_eugid(usr_attr.uid, usr_attr.gid); 186 vstring_sprintf(buf, "%lu.P%d.%s", 187 (unsigned long) starttime.tv_sec, var_pid, get_hostname()); 188 tmpfile = concatenate(tmpdir, STR(buf), (char *) 0); 189 newfile = 0; 190 if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0 191 && (errno != ENOENT 192 || make_dirs(tmpdir, 0700) < 0 193 || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) { 194 dsb_simple(why, mbox_dsn(errno, "5.2.0"), 195 "create maildir file %s: %m", tmpfile); 196 } else if (fstat(vstream_fileno(dst), &st) < 0) { 197 198 /* 199 * Coverity 200604: file descriptor leak in code that never executes. 200 * Code replaced by msg_fatal(), as it is not worthwhile to continue 201 * after an impossible error condition. 202 */ 203 msg_fatal("fstat %s: %m", tmpfile); 204 } else { 205 vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s", 206 (unsigned long) starttime.tv_sec, 207 (unsigned long) st.st_dev, 208 (unsigned long) st.st_ino, 209 (unsigned long) starttime.tv_usec, 210 get_hostname()); 211 newfile = concatenate(newdir, STR(buf), (char *) 0); 212 if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), 213 dst, copy_flags, "\n", 214 why)) == 0) { 215 if (sane_link(tmpfile, newfile) < 0 216 && (errno != ENOENT 217 || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0 218 || sane_link(tmpfile, newfile) < 0)) { 219 dsb_simple(why, mbox_dsn(errno, "5.2.0"), 220 "create maildir file %s: %m", newfile); 221 mail_copy_status = MAIL_COPY_STAT_WRITE; 222 } 223 } 224 if (unlink(tmpfile) < 0) 225 msg_warn("remove %s: %m", tmpfile); 226 } 227 set_eugid(var_owner_uid, var_owner_gid); 228 229 /* 230 * As the mail system, bounce or defer delivery. 231 */ 232 if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { 233 deliver_status = DEL_STAT_DEFER; 234 } else if (mail_copy_status != 0) { 235 if (errno == EACCES) { 236 msg_warn("maildir access problem for UID/GID=%lu/%lu: %s", 237 (long) usr_attr.uid, (long) usr_attr.gid, 238 STR(why->reason)); 239 msg_warn("perhaps you need to create the maildirs in advance"); 240 } 241 vstring_sprintf_prepend(why->reason, "maildir delivery failed: "); 242 deliver_status = 243 (STR(why->status)[0] == '4' ? 244 defer_append : bounce_append) 245 (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); 246 } else { 247 dsb_simple(why, "2.0.0", "delivered to maildir"); 248 deliver_status = sent(BOUNCE_FLAGS(state.request), 249 SENT_ATTR(state.msg_attr)); 250 } 251 vstring_free(buf); 252 myfree(newdir); 253 myfree(tmpdir); 254 myfree(curdir); 255 myfree(tmpfile); 256 if (newfile) 257 myfree(newfile); 258 return (deliver_status); 259 } 260