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