Postfix3.3.1
mail_copy.c
[詳解]
1 /*++
2 /* NAME
3 /* mail_copy 3
4 /* SUMMARY
5 /* copy message with extreme prejudice
6 /* SYNOPSIS
7 /* #include <mail_copy.h>
8 /*
9 /* int mail_copy(sender, orig_to, delivered, src, dst, flags, eol, why)
10 /* const char *sender;
11 /* const char *orig_to;
12 /* const char *delivered;
13 /* VSTREAM *src;
14 /* VSTREAM *dst;
15 /* int flags;
16 /* const char *eol;
17 /* DSN_BUF *why;
18 /* DESCRIPTION
19 /* mail_copy() copies a mail message from record stream to stream-lf
20 /* stream, and attempts to detect all possible I/O errors.
21 /*
22 /* Arguments:
23 /* .IP sender
24 /* The sender envelope address.
25 /* .IP delivered
26 /* Null pointer or delivered-to: header address.
27 /* .IP src
28 /* The source record stream, positioned at the beginning of the
29 /* message contents.
30 /* .IP dst
31 /* The destination byte stream (in stream-lf format). If the message
32 /* ends in an incomplete line, a newline character is appended to
33 /* the output.
34 /* .IP flags
35 /* The binary OR of zero or more of the following:
36 /* .RS
37 /* .IP MAIL_COPY_QUOTE
38 /* Prepend a `>' character to lines beginning with `From '.
39 /* .IP MAIL_COPY_DOT
40 /* Prepend a `.' character to lines beginning with `.'.
41 /* .IP MAIL_COPY_TOFILE
42 /* On systems that support this, use fsync() to flush the
43 /* data to stable storage, and truncate the destination
44 /* file to its original length in case of problems.
45 /* .IP MAIL_COPY_FROM
46 /* Prepend a UNIX-style From_ line to the message.
47 /* .IP MAIL_COPY_BLANK
48 /* Append an empty line to the end of the message.
49 /* .IP MAIL_COPY_DELIVERED
50 /* Prepend a Delivered-To: header with the name of the
51 /* \fIdelivered\fR attribute.
52 /* The address is quoted according to RFC822 rules.
53 /* .IP MAIL_COPY_ORIG_RCPT
54 /* Prepend an X-Original-To: header with the original
55 /* envelope recipient address. This is a NOOP with
56 /* var_enable_orcpt === 0.
57 /* .IP MAIL_COPY_RETURN_PATH
58 /* Prepend a Return-Path: header with the value of the
59 /* \fIsender\fR attribute.
60 /* .RE
61 /* The manifest constant MAIL_COPY_MBOX is a convenient shorthand for
62 /* all MAIL_COPY_XXX options that are appropriate for mailbox delivery.
63 /* Use MAIL_COPY_NONE to copy a message without any options enabled.
64 /* .IP eol
65 /* Record delimiter, for example, LF or CF LF.
66 /* .IP why
67 /* A null pointer, or storage for the reason of failure in
68 /* the form of a DSN detail code plus free text.
69 /* DIAGNOSTICS
70 /* A non-zero result means the operation failed. Warnings: corrupt
71 /* message file. A corrupt message is marked as corrupt.
72 /*
73 /* The result is the bit-wise OR of zero or more of the following:
74 /* .IP MAIL_COPY_STAT_CORRUPT
75 /* The queue file is marked as corrupt.
76 /* .IP MAIL_COPY_STAT_READ
77 /* A read error was detected; errno specifies the nature of the problem.
78 /* .IP MAIL_COPY_STAT_WRITE
79 /* A write error was detected; errno specifies the nature of the problem.
80 /* SEE ALSO
81 /* mark_corrupt(3), mark queue file as corrupted.
82 /* LICENSE
83 /* .ad
84 /* .fi
85 /* The Secure Mailer license must be distributed with this software.
86 /* AUTHOR(S)
87 /* Wietse Venema
88 /* IBM T.J. Watson Research
89 /* P.O. Box 704
90 /* Yorktown Heights, NY 10598, USA
91 /*
92 /* Wietse Venema
93 /* Google, Inc.
94 /* 111 8th Avenue
95 /* New York, NY 10011, USA
96 /*--*/
97 
98 /* System library. */
99 
100 #include <sys_defs.h>
101 #include <sys/stat.h>
102 #include <string.h>
103 #include <unistd.h>
104 #include <time.h>
105 #include <errno.h>
106 
107 /* Utility library. */
108 
109 #include <msg.h>
110 #include <htable.h>
111 #include <vstream.h>
112 #include <vstring.h>
113 #include <vstring_vstream.h>
114 #include <stringops.h>
115 #include <iostuff.h>
116 #include <warn_stat.h>
117 
118 /* Global library. */
119 
120 #include "quote_822_local.h"
121 #include "record.h"
122 #include "rec_type.h"
123 #include "mail_queue.h"
124 #include "mail_addr.h"
125 #include "mark_corrupt.h"
126 #include "mail_params.h"
127 #include "mail_copy.h"
128 #include "mbox_open.h"
129 #include "dsn_buf.h"
130 #include "sys_exits.h"
131 
132 /* mail_copy - copy message with extreme prejudice */
133 
134 int mail_copy(const char *sender,
135  const char *orig_rcpt,
136  const char *delivered,
137  VSTREAM *src, VSTREAM *dst,
138  int flags, const char *eol, DSN_BUF *why)
139 {
140  const char *myname = "mail_copy";
141  VSTRING *buf;
142  char *bp;
143  off_t orig_length;
144  int read_error;
145  int write_error;
146  int corrupt_error = 0;
147  time_t now;
148  int type;
149  int prev_type;
150  struct stat st;
151  off_t size_limit;
152 
153  /*
154  * Workaround 20090114. This will hopefully get someone's attention. The
155  * problem with file_size_limit < message_size_limit is that mail will be
156  * delivered again and again until someone removes it from the queue by
157  * hand, because Postfix cannot mark a recipient record as "completed".
158  */
159  if (fstat(vstream_fileno(src), &st) < 0)
160  msg_fatal("fstat: %m");
161  if ((size_limit = get_file_limit()) < st.st_size)
162  msg_panic("file size limit %lu < message size %lu. This "
163  "causes large messages to be delivered repeatedly "
164  "after they were submitted with \"sendmail -t\" "
165  "or after recipients were added with the Milter "
166  "SMFIR_ADDRCPT request",
167  (unsigned long) size_limit,
168  (unsigned long) st.st_size);
169 
170  /*
171  * Initialize.
172  */
173 #ifndef NO_TRUNCATE
174  if ((flags & MAIL_COPY_TOFILE) != 0)
175  if ((orig_length = vstream_fseek(dst, (off_t) 0, SEEK_END)) < 0)
176  msg_fatal("seek file %s: %m", VSTREAM_PATH(dst));
177 #endif
178  buf = vstring_alloc(100);
179 
180  /*
181  * Prepend a bunch of headers to the message.
182  */
183  if (flags & (MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH)) {
184  if (sender == 0)
185  msg_panic("%s: null sender", myname);
186  quote_822_local(buf, sender);
187  if (flags & MAIL_COPY_FROM) {
188  time(&now);
189  vstream_fprintf(dst, "From %s %.24s%s", *sender == 0 ?
191  asctime(localtime(&now)), eol);
192  }
193  if (flags & MAIL_COPY_RETURN_PATH) {
194  vstream_fprintf(dst, "Return-Path: <%s>%s",
195  *sender ? vstring_str(buf) : "", eol);
196  }
197  }
198  if (flags & MAIL_COPY_ORIG_RCPT) {
199  if (orig_rcpt == 0)
200  msg_panic("%s: null orig_rcpt", myname);
201 
202  /*
203  * An empty original recipient record almost certainly means that
204  * original recipient processing was disabled.
205  */
206  if (var_enable_orcpt && *orig_rcpt) {
207  quote_822_local(buf, orig_rcpt);
208  vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol);
209  }
210  }
211  if (flags & MAIL_COPY_DELIVERED) {
212  if (delivered == 0)
213  msg_panic("%s: null delivered", myname);
214  quote_822_local(buf, delivered);
215  vstream_fprintf(dst, "Delivered-To: %s%s", vstring_str(buf), eol);
216  }
217 
218  /*
219  * Copy the message. Escape lines that could be confused with the ugly
220  * From_ line. Make sure that there is a blank line at the end of the
221  * message so that the next ugly From_ can be found by mail reading
222  * software.
223  *
224  * XXX Rely on the front-end services to enforce record size limits.
225  */
226 #define VSTREAM_FWRITE_BUF(s,b) \
227  vstream_fwrite((s),vstring_str(b),VSTRING_LEN(b))
228 
229  prev_type = REC_TYPE_NORM;
230  while ((type = rec_get(src, buf, 0)) > 0) {
231  if (type != REC_TYPE_NORM && type != REC_TYPE_CONT)
232  break;
233  bp = vstring_str(buf);
234  if (prev_type == REC_TYPE_NORM) {
235  if ((flags & MAIL_COPY_QUOTE) && *bp == 'F' && !strncmp(bp, "From ", 5))
236  VSTREAM_PUTC('>', dst);
237  if ((flags & MAIL_COPY_DOT) && *bp == '.')
238  VSTREAM_PUTC('.', dst);
239  }
240  if (VSTRING_LEN(buf) && VSTREAM_FWRITE_BUF(dst, buf) != VSTRING_LEN(buf))
241  break;
242  if (type == REC_TYPE_NORM && vstream_fputs(eol, dst) == VSTREAM_EOF)
243  break;
244  prev_type = type;
245  }
246  if (vstream_ferror(dst) == 0) {
247  if (var_fault_inj_code == 1)
248  type = 0;
249  if (type != REC_TYPE_XTRA) {
250  /* XXX Where is the queue ID? */
251  msg_warn("bad record type: %d in message content", type);
252  corrupt_error = mark_corrupt(src);
253  }
254  if (prev_type != REC_TYPE_NORM)
255  vstream_fputs(eol, dst);
256  if (flags & MAIL_COPY_BLANK)
257  vstream_fputs(eol, dst);
258  }
259  vstring_free(buf);
260 
261  /*
262  * Make sure we read and wrote all. Truncate the file to its original
263  * length when the delivery failed. POSIX does not require ftruncate(),
264  * so we may have a portability problem. Note that fclose() may fail even
265  * while fflush and fsync() succeed. Think of remote file systems such as
266  * AFS that copy the file back to the server upon close. Oh well, no
267  * point optimizing the error case. XXX On systems that use flock()
268  * locking, we must truncate the file file before closing it (and losing
269  * the exclusive lock).
270  */
271  read_error = vstream_ferror(src);
272  write_error = vstream_fflush(dst);
273 #ifdef HAS_FSYNC
274  if ((flags & MAIL_COPY_TOFILE) != 0)
275  write_error |= fsync(vstream_fileno(dst));
276 #endif
277  if (var_fault_inj_code == 2) {
278  read_error = 1;
279  errno = ENOENT;
280  }
281  if (var_fault_inj_code == 3) {
282  write_error = 1;
283  errno = ENOENT;
284  }
285 #ifndef NO_TRUNCATE
286  if ((flags & MAIL_COPY_TOFILE) != 0)
287  if (corrupt_error || read_error || write_error)
288  /* Complain about ignored "undo" errors? So sue me. */
289  (void) ftruncate(vstream_fileno(dst), orig_length);
290 #endif
291  write_error |= vstream_fclose(dst);
292 
293  /*
294  * Return the optional verbose error description.
295  */
296 #define TRY_AGAIN_ERROR(errno) \
297  (errno == EAGAIN || errno == ESTALE)
298 
299  if (why && read_error)
300  dsb_unix(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0",
301  sys_exits_detail(EX_IOERR)->text,
302  "error reading message: %m");
303  if (why && write_error)
304  dsb_unix(why, mbox_dsn(errno, "5.3.0"),
305  sys_exits_detail(EX_IOERR)->text,
306  "error writing message: %m");
307 
308  /*
309  * Use flag+errno description when the optional verbose description is
310  * not desired.
311  */
312  return ((corrupt_error ? MAIL_COPY_STAT_CORRUPT : 0)
313  | (read_error ? MAIL_COPY_STAT_READ : 0)
314  | (write_error ? MAIL_COPY_STAT_WRITE : 0));
315 }
#define VSTREAM_EOF
Definition: vstream.h:110
const char * mbox_dsn(int err, const char *def_dsn)
Definition: mbox_open.c:243
#define MAIL_COPY_QUOTE
Definition: mail_copy.h:32
int var_fault_inj_code
Definition: mail_params.c:300
#define MAIL_COPY_STAT_WRITE
Definition: mail_copy.h:50
#define MAIL_COPY_DOT
Definition: mail_copy.h:37
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
#define stat(p, s)
Definition: warn_stat.h:18
#define EX_IOERR
Definition: sys_exits.h:41
#define MAIL_COPY_RETURN_PATH
Definition: mail_copy.h:36
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define MAIL_COPY_STAT_READ
Definition: mail_copy.h:49
off_t get_file_limit(void)
Definition: file_limit.c:58
#define MAIL_COPY_BLANK
Definition: mail_copy.h:38
VSTREAM * vstream_fprintf(VSTREAM *stream, const char *fmt,...)
Definition: vstream.c:1348
#define REC_TYPE_CONT
Definition: rec_type.h:58
#define TRY_AGAIN_ERROR(errno)
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
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
#define MAIL_COPY_FROM
Definition: mail_copy.h:34
#define MAIL_ADDR_MAIL_DAEMON
Definition: mail_addr.h:18
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
bool var_enable_orcpt
Definition: mail_params.c:349
#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
#define VSTREAM_FWRITE_BUF(s, b)
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
#define MAIL_COPY_TOFILE
Definition: mail_copy.h:33
#define quote_822_local(dst, src)
DSN_BUF * dsb_unix(DSN_BUF *dsb, const char *status, const char *dtext, const char *format,...)
Definition: dsn_buf.c:287
#define REC_TYPE_XTRA
Definition: rec_type.h:62
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define vstream_fileno(vp)
Definition: vstream.h:115
int mark_corrupt(VSTREAM *src)
Definition: mark_corrupt.c:47
#define REC_TYPE_NORM
Definition: rec_type.h:59
#define MAIL_COPY_STAT_CORRUPT
Definition: mail_copy.h:48
#define VSTREAM_PUTC(ch, vp)
Definition: vstream.h:107
const SYS_EXITS_DETAIL * sys_exits_detail(int code)
Definition: sys_exits.c:125
#define MAIL_COPY_ORIG_RCPT
Definition: mail_copy.h:39
#define rec_get(fp, buf, limit)
Definition: record.h:56
#define vstream_ferror(vp)
Definition: vstream.h:120
#define fstat(f, s)
Definition: warn_stat.h:20
int vstream_fputs(const char *str, VSTREAM *stream)
Definition: vstream.c:1360