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