11 /* int deliver_maildir(state, usr_attr, path)
13 /* USER_ATTR usr_attr;
16 /* deliver_maildir() delivers a message to a qmail maildir.
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.
24 /* Attributes describing user rights and environment information.
26 /* The maildir to deliver to, including trailing slash.
28 /* deliver_maildir() always succeeds or it bounces the message.
34 /* The Secure Mailer license must be distributed with this software.
37 /* IBM T.J. Watson Research
39 /* Yorktown Heights, NY 10598, USA
51 /* Utility library. */
55 #include <stringops.h>
58 #include <make_dirs.h>
59 #include <set_eugid.h>
60 #include <get_hostname.h>
61 #include <sane_fsops.h>
65 #include <mail_copy.h>
69 #include <mail_params.h>
71 #include <mbox_open.h>
73 /* Application-specific. */
77 /* deliver_maildir - delivery to maildir-style mailbox */
79 int deliver_maildir(LOCAL_STATE state
, USER_ATTR usr_attr
, char *path
)
81 const char *myname
= "deliver_maildir";
87 DSN_BUF
*why
= state
.msg_attr
.why
;
94 struct timeval starttime
;
96 GETTIMEOFDAY(&starttime
);
99 * Make verbose logging easier to understand.
103 MSG_LOG_STATE(myname
, state
);
106 * Don't deliver trace-only requests.
108 if (DEL_REQ_TRACE_ONLY(state
.request
->flags
)) {
109 dsb_simple(why
, "2.0.0", "delivers to maildir");
110 return (sent(BOUNCE_FLAGS(state
.request
), SENT_ATTR(state
.msg_attr
)));
114 * Initialize. Assume the operation will fail. Set the delivered
115 * attribute to reflect the final recipient.
117 if (vstream_fseek(state
.msg_attr
.fp
, state
.msg_attr
.offset
, SEEK_SET
) < 0)
118 msg_fatal("seek message file %s: %m", VSTREAM_PATH(state
.msg_attr
.fp
));
119 if (var_frozen_delivered
== 0)
120 state
.msg_attr
.delivered
= state
.msg_attr
.rcpt
.address
;
121 mail_copy_status
= MAIL_COPY_STAT_WRITE
;
122 buf
= vstring_alloc(100);
124 copy_flags
= MAIL_COPY_TOFILE
| MAIL_COPY_RETURN_PATH
| MAIL_COPY_ORIG_RCPT
;
125 if (local_deliver_hdr_mask
& DELIVER_HDR_FILE
)
126 copy_flags
|= MAIL_COPY_DELIVERED
;
128 newdir
= concatenate(path
, "new/", (char *) 0);
129 tmpdir
= concatenate(path
, "tmp/", (char *) 0);
130 curdir
= concatenate(path
, "cur/", (char *) 0);
133 * Create and write the file as the recipient, so that file quota work.
134 * Create any missing directories on the fly. The file name is chosen
135 * according to ftp://koobera.math.uic.edu/www/proto/maildir.html:
137 * "A unique name has three pieces, separated by dots. On the left is the
138 * result of time(). On the right is the result of gethostname(). In the
139 * middle is something that doesn't repeat within one second on a single
140 * host. I fork a new process for each delivery, so I just use the
141 * process ID. If you're delivering several messages from one process,
142 * use starttime.pid_count.host, where starttime is the time that your
143 * process started, and count is the number of messages you've
146 * Well, that stopped working on fast machines, and on operating systems
147 * that randomize process ID values. When creating a file in tmp/ we use
148 * the process ID because it still is an exclusive resource. When moving
149 * the file to new/ we use the device number and inode number. I do not
150 * care if this breaks on a remote AFS file system, because people should
153 * On January 26, 2003, http://cr.yp.to/proto/maildir.html said:
155 * A unique name has three pieces, separated by dots. On the left is the
156 * result of time() or the second counter from gettimeofday(). On the
157 * right is the result of gethostname(). (To deal with invalid host
158 * names, replace / with \057 and : with \072.) In the middle is a
159 * delivery identifier, discussed below.
163 * Modern delivery identifiers are created by concatenating enough of the
164 * following strings to guarantee uniqueness:
168 * In, where n is (in hexadecimal) the UNIX inode number of this file.
169 * Unfortunately, inode numbers aren't always available through NFS.
171 * Vn, where n is (in hexadecimal) the UNIX device number of this file.
172 * Unfortunately, device numbers aren't always available through NFS.
173 * (Device numbers are also not helpful with the standard UNIX
174 * filesystem: a maildir has to be within a single UNIX device for link()
175 * and rename() to work.)
177 * Mn, where n is (in decimal) the microsecond counter from the same
178 * gettimeofday() used for the left part of the unique name.
180 * Pn, where n is (in decimal) the process ID.
184 set_eugid(usr_attr
.uid
, usr_attr
.gid
);
185 vstring_sprintf(buf
, "%lu.P%d.%s",
186 (unsigned long) starttime
.tv_sec
, var_pid
, get_hostname());
187 tmpfile
= concatenate(tmpdir
, STR(buf
), (char *) 0);
189 if ((dst
= vstream_fopen(tmpfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600)) == 0
191 || make_dirs(tmpdir
, 0700) < 0
192 || (dst
= vstream_fopen(tmpfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0600)) == 0)) {
193 dsb_simple(why
, mbox_dsn(errno
, "5.2.0"),
194 "create maildir file %s: %m", tmpfile
);
195 } else if (fstat(vstream_fileno(dst
), &st
) < 0) {
198 * Coverity 200604: file descriptor leak in code that never executes.
199 * Code replaced by msg_fatal(), as it is not worthwhile to continue
200 * after an impossible error condition.
202 msg_fatal("fstat %s: %m", tmpfile
);
204 vstring_sprintf(buf
, "%lu.V%lxI%lxM%lu.%s",
205 (unsigned long) starttime
.tv_sec
,
206 (unsigned long) st
.st_dev
,
207 (unsigned long) st
.st_ino
,
208 (unsigned long) starttime
.tv_usec
,
210 newfile
= concatenate(newdir
, STR(buf
), (char *) 0);
211 if ((mail_copy_status
= mail_copy(COPY_ATTR(state
.msg_attr
),
212 dst
, copy_flags
, "\n",
214 if (sane_link(tmpfile
, newfile
) < 0
216 || (make_dirs(curdir
, 0700), make_dirs(newdir
, 0700)) < 0
217 || sane_link(tmpfile
, newfile
) < 0)) {
218 dsb_simple(why
, mbox_dsn(errno
, "5.2.0"),
219 "create maildir file %s: %m", newfile
);
220 mail_copy_status
= MAIL_COPY_STAT_WRITE
;
223 if (unlink(tmpfile
) < 0)
224 msg_warn("remove %s: %m", tmpfile
);
226 set_eugid(var_owner_uid
, var_owner_gid
);
229 * As the mail system, bounce or defer delivery.
231 if (mail_copy_status
& MAIL_COPY_STAT_CORRUPT
) {
232 deliver_status
= DEL_STAT_DEFER
;
233 } else if (mail_copy_status
!= 0) {
234 if (errno
== EACCES
) {
235 msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
236 (long) usr_attr
.uid
, (long) usr_attr
.gid
,
238 msg_warn("perhaps you need to create the maildirs in advance");
240 vstring_sprintf_prepend(why
->reason
, "maildir delivery failed: ");
242 (STR(why
->status
)[0] == '4' ?
243 defer_append
: bounce_append
)
244 (BOUNCE_FLAGS(state
.request
), BOUNCE_ATTR(state
.msg_attr
));
246 dsb_simple(why
, "2.0.0", "delivered to maildir");
247 deliver_status
= sent(BOUNCE_FLAGS(state
.request
),
248 SENT_ATTR(state
.msg_attr
));
257 return (deliver_status
);