1 /* $NetBSD: bounce_append_service.c,v 1.2 2017/02/14 01:16:44 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* bounce_append_service 3
6 /* SUMMARY
7 /* append record to bounce log, server side
8 /* SYNOPSIS
9 /* #include "bounce_service.h"
10 /*
11 /* int bounce_append_service(flags, service, queue_id, rcpt, dsn),
12 /* int flags;
13 /* char *service;
14 /* char *queue_id;
15 /* RECIPIENT *rcpt;
16 /* DSN *dsn;
17 /* DESCRIPTION
18 /* This module implements the server side of the bounce_append()
19 /* (append bounce log) request. This routine either succeeds or
20 /* it raises a fatal error.
21 /* DIAGNOSTICS
22 /* Fatal errors: all file access errors; memory allocation errors.
23 /* BUGS
24 /* SEE ALSO
25 /* bounce(3) basic bounce service client interface
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 <unistd.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <ctype.h>
44 #include <string.h>
45
46 /* Utility library. */
47
48 #include <msg.h>
49 #include <vstring.h>
50 #include <vstream.h>
51 #include <stringops.h>
52
53 /* Global library. */
54
55 #include <mail_params.h>
56 #include <mail_queue.h>
57 #include <quote_822_local.h>
58 #include <deliver_flock.h>
59 #include <mail_proto.h>
60
61 /* Application-specific. */
62
63 #include "bounce_service.h"
64
65 /* bounce_append_service - append bounce log */
66
bounce_append_service(int unused_flags,char * service,char * queue_id,RECIPIENT * rcpt,DSN * dsn)67 int bounce_append_service(int unused_flags, char *service, char *queue_id,
68 RECIPIENT *rcpt, DSN *dsn)
69 {
70 VSTRING *in_buf = vstring_alloc(100);
71 VSTREAM *log;
72 long orig_length;
73
74 /*
75 * This code is paranoid for a good reason. Once the bounce service takes
76 * responsibility, the mail system will make no further attempts to
77 * deliver this recipient. Whenever file access fails, assume that the
78 * system is under stress or that something has been mis-configured, and
79 * force a backoff by raising a fatal run-time error.
80 */
81 log = mail_queue_open(service, queue_id,
82 O_WRONLY | O_APPEND | O_CREAT, 0600);
83 if (log == 0)
84 msg_fatal("open file %s %s: %m", service, queue_id);
85
86 /*
87 * Lock out other processes to avoid truncating someone else's data in
88 * case of trouble.
89 */
90 if (deliver_flock(vstream_fileno(log), INTERNAL_LOCK, (VSTRING *) 0) < 0)
91 msg_fatal("lock file %s %s: %m", service, queue_id);
92
93 /*
94 * Now, go for it. Append a record. Truncate the log to the original
95 * length when the append operation fails. We use the plain stream-lf
96 * file format because we do not need anything more complicated. As a
97 * benefit, we can still recover some data when the file is a little
98 * garbled.
99 *
100 * XXX addresses in defer logfiles are in printable quoted form, while
101 * addresses in message envelope records are in raw unquoted form. This
102 * may change once we replace the present ad-hoc bounce/defer logfile
103 * format by one that is transparent for control etc. characters. See
104 * also: showq/showq.c.
105 *
106 * While migrating from old format to new format, allow backwards
107 * compatibility by writing an old-style record before the new-style
108 * records.
109 */
110 if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0)
111 msg_fatal("seek file %s %s: %m", service, queue_id);
112
113 #define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0)
114 #define STR(x) vstring_str(x)
115
116 vstream_fputs("\n", log);
117 if (var_oldlog_compat) {
118 vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" :
119 STR(quote_822_local(in_buf, rcpt->address)),
120 dsn->reason);
121 }
122 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ?
123 STR(quote_822_local(in_buf, rcpt->address)) : "<>");
124 if (NOT_NULL_EMPTY(rcpt->orig_addr)
125 && strcasecmp_utf8(rcpt->address, rcpt->orig_addr) != 0)
126 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT,
127 STR(quote_822_local(in_buf, rcpt->orig_addr)));
128 if (rcpt->offset > 0)
129 vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset);
130 if (NOT_NULL_EMPTY(rcpt->dsn_orcpt))
131 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt);
132 if (rcpt->dsn_notify != 0)
133 vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify);
134
135 if (NOT_NULL_EMPTY(dsn->status))
136 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, dsn->status);
137 if (NOT_NULL_EMPTY(dsn->action))
138 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, dsn->action);
139 if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) {
140 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, dsn->dtype);
141 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, dsn->dtext);
142 }
143 if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) {
144 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, dsn->mtype);
145 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, dsn->mname);
146 }
147 if (NOT_NULL_EMPTY(dsn->reason))
148 vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, dsn->reason);
149 vstream_fputs("\n", log);
150
151 if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) {
152 #ifndef NO_TRUNCATE
153 if (ftruncate(vstream_fileno(log), (off_t) orig_length) < 0)
154 msg_fatal("truncate file %s %s: %m", service, queue_id);
155 #endif
156 msg_fatal("append file %s %s: %m", service, queue_id);
157 }
158
159 /*
160 * Darn. If closing the log detects a problem, the only way to undo the
161 * damage is to open the log once more, and to truncate the log to the
162 * original length. But, this could happen only when the log is kept on a
163 * remote file system, and that is not recommended practice anyway.
164 */
165 if (vstream_fclose(log) != 0)
166 msg_warn("append file %s %s: %m", service, queue_id);
167
168 vstring_free(in_buf);
169 return (0);
170 }
171