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