9 /* #include "virtual.h"
11 /* int deliver_maildir(state, usr_attr)
13 /* USER_ATTR usr_attr;
15 /* deliver_maildir() delivers a message to a qmail-style maildir.
19 /* The attributes that specify the message, recipient and more.
21 /* Attributes describing user rights and environment information.
23 /* deliver_maildir() always succeeds or it bounces the message.
29 /* The Secure Mailer license must be distributed with this software.
32 /* IBM T.J. Watson Research
34 /* Yorktown Heights, NY 10598, USA
46 /* Utility library. */
50 #include <stringops.h>
53 #include <make_dirs.h>
54 #include <set_eugid.h>
55 #include <get_hostname.h>
56 #include <sane_fsops.h>
60 #include <mail_copy.h>
64 #include <mail_params.h>
65 #include <mbox_open.h>
68 /* Application-specific. */
72 /* deliver_maildir - delivery to maildir-style mailbox */
74 int deliver_maildir(LOCAL_STATE state
, USER_ATTR usr_attr
)
76 const char *myname
= "deliver_maildir";
82 DSN_BUF
*why
= state
.msg_attr
.why
;
89 struct timeval starttime
;
91 GETTIMEOFDAY(&starttime
);
94 * Make verbose logging easier to understand.
98 MSG_LOG_STATE(myname
, state
);
101 * Don't deliver trace-only requests.
103 if (DEL_REQ_TRACE_ONLY(state
.request
->flags
)) {
104 dsb_simple(why
, "2.0.0", "delivers to maildir");
105 return (sent(BOUNCE_FLAGS(state
.request
),
106 SENT_ATTR(state
.msg_attr
)));
110 * Initialize. Assume the operation will fail. Set the delivered
111 * attribute to reflect the final recipient.
113 if (vstream_fseek(state
.msg_attr
.fp
, state
.msg_attr
.offset
, SEEK_SET
) < 0)
114 msg_fatal("seek message file %s: %m", VSTREAM_PATH(state
.msg_attr
.fp
));
115 state
.msg_attr
.delivered
= state
.msg_attr
.rcpt
.address
;
116 mail_copy_status
= MAIL_COPY_STAT_WRITE
;
117 buf
= vstring_alloc(100);
119 copy_flags
= MAIL_COPY_TOFILE
| MAIL_COPY_RETURN_PATH
120 | MAIL_COPY_DELIVERED
| MAIL_COPY_ORIG_RCPT
;
122 newdir
= concatenate(usr_attr
.mailbox
, "new/", (char *) 0);
123 tmpdir
= concatenate(usr_attr
.mailbox
, "tmp/", (char *) 0);
124 curdir
= concatenate(usr_attr
.mailbox
, "cur/", (char *) 0);
127 * Create and write the file as the recipient, so that file quota work.
128 * Create any missing directories on the fly. The file name is chosen
129 * according to ftp://koobera.math.uic.edu/www/proto/maildir.html:
131 * "A unique name has three pieces, separated by dots. On the left is the
132 * result of time(). On the right is the result of gethostname(). In the
133 * middle is something that doesn't repeat within one second on a single
134 * host. I fork a new process for each delivery, so I just use the
135 * process ID. If you're delivering several messages from one process,
136 * use starttime.pid_count.host, where starttime is the time that your
137 * process started, and count is the number of messages you've
140 * Well, that stopped working on fast machines, and on operating systems
141 * that randomize process ID values. When creating a file in tmp/ we use
142 * the process ID because it still is an exclusive resource. When moving
143 * the file to new/ we use the device number and inode number. I do not
144 * care if this breaks on a remote AFS file system, because people should
147 * On January 26, 2003, http://cr.yp.to/proto/maildir.html said:
149 * A unique name has three pieces, separated by dots. On the left is the
150 * result of time() or the second counter from gettimeofday(). On the
151 * right is the result of gethostname(). (To deal with invalid host
152 * names, replace / with \057 and : with \072.) In the middle is a
153 * delivery identifier, discussed below.
157 * Modern delivery identifiers are created by concatenating enough of the
158 * following strings to guarantee uniqueness:
162 * In, where n is (in hexadecimal) the UNIX inode number of this file.
163 * Unfortunately, inode numbers aren't always available through NFS.
165 * Vn, where n is (in hexadecimal) the UNIX device number of this file.
166 * Unfortunately, device numbers aren't always available through NFS.
167 * (Device numbers are also not helpful with the standard UNIX
168 * filesystem: a maildir has to be within a single UNIX device for link()
169 * and rename() to work.)
171 * Mn, where n is (in decimal) the microsecond counter from the same
172 * gettimeofday() used for the left part of the unique name.
174 * Pn, where n is (in decimal) the process ID.
178 set_eugid(usr_attr
.uid
, usr_attr
.gid
);
179 vstring_sprintf(buf
, "%lu.P%d.%s",
180 (unsigned long) starttime
.tv_sec
, var_pid
, get_hostname());
181 tmpfile
= concatenate(tmpdir
, STR(buf
), (char *) 0);
183 if ((dst
= vstream_fopen(tmpfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600)) == 0
185 || make_dirs(tmpdir
, 0700) < 0
186 || (dst
= vstream_fopen(tmpfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600)) == 0)) {
187 dsb_simple(why
, mbox_dsn(errno
, "4.2.0"),
188 "create maildir file %s: %m", tmpfile
);
189 } else if (fstat(vstream_fileno(dst
), &st
) < 0) {
192 * Coverity 200604: file descriptor leak in code that never executes.
193 * Code replaced by msg_fatal(), as it is not worthwhile to continue
194 * after an impossible error condition.
196 msg_fatal("fstat %s: %m", tmpfile
);
198 vstring_sprintf(buf
, "%lu.V%lxI%lxM%lu.%s",
199 (unsigned long) starttime
.tv_sec
,
200 (unsigned long) st
.st_dev
,
201 (unsigned long) st
.st_ino
,
202 (unsigned long) starttime
.tv_usec
,
204 newfile
= concatenate(newdir
, STR(buf
), (char *) 0);
205 if ((mail_copy_status
= mail_copy(COPY_ATTR(state
.msg_attr
),
206 dst
, copy_flags
, "\n",
208 if (sane_link(tmpfile
, newfile
) < 0
210 || (make_dirs(curdir
, 0700), make_dirs(newdir
, 0700)) < 0
211 || sane_link(tmpfile
, newfile
) < 0)) {
212 dsb_simple(why
, mbox_dsn(errno
, "4.2.0"),
213 "create maildir file %s: %m", newfile
);
214 mail_copy_status
= MAIL_COPY_STAT_WRITE
;
217 if (unlink(tmpfile
) < 0)
218 msg_warn("remove %s: %m", tmpfile
);
220 set_eugid(var_owner_uid
, var_owner_gid
);
223 * The maildir location is controlled by the mail administrator. If
224 * delivery fails, try again later. We would just bounce when the maildir
225 * location possibly under user control.
227 if (mail_copy_status
& MAIL_COPY_STAT_CORRUPT
) {
228 deliver_status
= DEL_STAT_DEFER
;
229 } else if (mail_copy_status
!= 0) {
230 if (errno
== EACCES
) {
231 msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
232 (long) usr_attr
.uid
, (long) usr_attr
.gid
,
234 msg_warn("perhaps you need to create the maildirs in advance");
236 vstring_sprintf_prepend(why
->reason
, "maildir delivery failed: ");
238 (STR(why
->status
)[0] == '4' ?
239 defer_append
: bounce_append
)
240 (BOUNCE_FLAGS(state
.request
),
241 BOUNCE_ATTR(state
.msg_attr
));
243 dsb_simple(why
, "2.0.0", "delivered to maildir");
244 deliver_status
= sent(BOUNCE_FLAGS(state
.request
),
245 SENT_ATTR(state
.msg_attr
));
254 return (deliver_status
);