xref: /netbsd-src/external/ibm-public/postfix/dist/src/local/maildir.c (revision a30b880ed60a24c405edba78187a04247f4d9d33)
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 
deliver_maildir(LOCAL_STATE state,USER_ATTR usr_attr,char * path)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