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