1 /*++
2 /* NAME
3 /* forward 3
4 /* SUMMARY
5 /* message forwarding
6 /* SYNOPSIS
7 /* #include "local.h"
8 /*
9 /* int forward_init()
10 /*
11 /* int forward_append(attr)
12 /* DELIVER_ATTR attr;
13 /*
14 /* int forward_finish(request, attr, cancel)
15 /* DELIVER_REQUEST *request;
16 /* DELIVER_ATTR attr;
17 /* int cancel;
18 /* DESCRIPTION
19 /* This module implements the client interface for message
20 /* forwarding.
21 /*
22 /* forward_init() initializes internal data structures.
23 /*
24 /* forward_append() appends a recipient to the list of recipients
25 /* that will receive a message with the specified message sender
26 /* and delivered-to addresses.
27 /*
28 /* forward_finish() forwards the actual message contents and
29 /* releases the memory allocated by forward_init() and by
30 /* forward_append(). When the \fIcancel\fR argument is true, no
31 /* messages will be forwarded. The \fIattr\fR argument specifies
32 /* the original message delivery attributes as they were before
33 /* alias or forward expansions.
34 /* DIAGNOSTICS
35 /* A non-zero result means that the requested operation should
36 /* be tried again.
37 /* Warnings: problems connecting to the forwarding service,
38 /* corrupt message file. A corrupt message is saved to the
39 /* "corrupt" queue for further inspection.
40 /* Fatal: out of memory.
41 /* Panic: missing forward_init() or forward_finish() call.
42 /* LICENSE
43 /* .ad
44 /* .fi
45 /* The Secure Mailer license must be distributed with this software.
46 /* AUTHOR(S)
47 /* Wietse Venema
48 /* IBM T.J. Watson Research
49 /* P.O. Box 704
50 /* Yorktown Heights, NY 10598, USA
51 /*
52 /* Wietse Venema
53 /* Google, Inc.
54 /* 111 8th Avenue
55 /* New York, NY 10011, USA
56 /*--*/
57
58 /* System library. */
59
60 #include <sys_defs.h>
61 #include <sys/time.h>
62 #include <unistd.h>
63
64 /* Utility library. */
65
66 #include <msg.h>
67 #include <mymalloc.h>
68 #include <htable.h>
69 #include <argv.h>
70 #include <vstring.h>
71 #include <vstream.h>
72 #include <vstring_vstream.h>
73 #include <iostuff.h>
74 #include <stringops.h>
75
76 /* Global library. */
77
78 #include <mail_proto.h>
79 #include <cleanup_user.h>
80 #include <sent.h>
81 #include <record.h>
82 #include <rec_type.h>
83 #include <mark_corrupt.h>
84 #include <mail_date.h>
85 #include <mail_params.h>
86 #include <dsn_mask.h>
87 #include <smtputf8.h>
88
89 /* Application-specific. */
90
91 #include "local.h"
92
93 /*
94 * Use one cleanup service connection for each (delivered to, sender) pair.
95 */
96 static HTABLE *forward_dt;
97
98 typedef struct FORWARD_INFO {
99 VSTREAM *cleanup; /* clean up service handle */
100 char *queue_id; /* forwarded message queue id */
101 struct timeval posting_time; /* posting time */
102 } FORWARD_INFO;
103
104 /* forward_init - prepare for forwarding */
105
forward_init(void)106 int forward_init(void)
107 {
108
109 /*
110 * Sanity checks.
111 */
112 if (forward_dt != 0)
113 msg_panic("forward_init: missing forward_finish call");
114
115 forward_dt = htable_create(0);
116 return (0);
117 }
118
119 /* forward_open - open connection to cleanup service */
120
forward_open(DELIVER_REQUEST * request,const char * sender)121 static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
122 {
123 VSTRING *buffer = vstring_alloc(100);
124 FORWARD_INFO *info;
125 VSTREAM *cleanup;
126
127 #define FORWARD_OPEN_RETURN(res) do { \
128 vstring_free(buffer); \
129 return (res); \
130 } while (0)
131
132 /*
133 * Contact the cleanup service and save the new mail queue id. Request
134 * that the cleanup service bounces bad messages to the sender so that we
135 * can avoid the trouble of bounce management.
136 *
137 * In case you wonder what kind of bounces, examples are "too many hops",
138 * "message too large", perhaps some others. The reason not to bounce
139 * ourselves is that we don't really know who the recipients are.
140 */
141 cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING);
142 if (cleanup == 0) {
143 msg_warn("connect to %s/%s: %m",
144 MAIL_CLASS_PUBLIC, var_cleanup_service);
145 FORWARD_OPEN_RETURN(0);
146 }
147 close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC);
148 if (attr_scan(cleanup, ATTR_FLAG_STRICT,
149 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_CLEANUP),
150 RECV_ATTR_STR(MAIL_ATTR_QUEUEID, buffer),
151 ATTR_TYPE_END) != 1) {
152 vstream_fclose(cleanup);
153 FORWARD_OPEN_RETURN(0);
154 }
155 info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO));
156 info->cleanup = cleanup;
157 info->queue_id = mystrdup(STR(buffer));
158 GETTIMEOFDAY(&info->posting_time);
159
160 #define FORWARD_CLEANUP_FLAGS \
161 (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL \
162 | smtputf8_autodetect(MAIL_SRC_MASK_FORWARD) \
163 | ((request->smtputf8 & SMTPUTF8_FLAG_REQUESTED) ? \
164 CLEANUP_FLAG_SMTPUTF8 : 0))
165
166 attr_print(cleanup, ATTR_FLAG_NONE,
167 SEND_ATTR_INT(MAIL_ATTR_FLAGS, FORWARD_CLEANUP_FLAGS),
168 ATTR_TYPE_END);
169
170 /*
171 * Send initial message envelope information. For bounces, set the
172 * designated sender: mailing list owner, posting user, whatever.
173 */
174 rec_fprintf(cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
175 REC_TYPE_TIME_ARG(info->posting_time));
176 rec_fputs(cleanup, REC_TYPE_FROM, sender);
177
178 /*
179 * Don't send the original envelope ID or full/headers return mask if it
180 * was reset due to mailing list expansion.
181 */
182 if (request->dsn_ret)
183 rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%d",
184 MAIL_ATTR_DSN_RET, request->dsn_ret);
185 if (request->dsn_envid && *(request->dsn_envid))
186 rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%s",
187 MAIL_ATTR_DSN_ENVID, request->dsn_envid);
188
189 /*
190 * Zero-length attribute values are place holders for unavailable
191 * attribute values. See qmgr_message.c. They are not meant to be
192 * propagated to queue files.
193 */
194 #define PASS_ATTR(fp, name, value) do { \
195 if ((value) && *(value)) \
196 rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \
197 } while (0)
198
199 /*
200 * XXX encapsulate these as one object.
201 */
202 PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name);
203 PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr);
204 PASS_ATTR(cleanup, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto);
205 PASS_ATTR(cleanup, MAIL_ATTR_LOG_HELO_NAME, request->client_helo);
206 PASS_ATTR(cleanup, MAIL_ATTR_SASL_METHOD, request->sasl_method);
207 PASS_ATTR(cleanup, MAIL_ATTR_SASL_USERNAME, request->sasl_username);
208 PASS_ATTR(cleanup, MAIL_ATTR_SASL_SENDER, request->sasl_sender);
209 PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident);
210 PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context);
211
212 FORWARD_OPEN_RETURN(info);
213 }
214
215 /* forward_append - append recipient to message envelope */
216
forward_append(DELIVER_ATTR attr)217 int forward_append(DELIVER_ATTR attr)
218 {
219 FORWARD_INFO *info;
220 HTABLE *table_snd;
221
222 /*
223 * Sanity checks.
224 */
225 if (msg_verbose)
226 msg_info("forward delivered=%s sender=%s recip=%s",
227 attr.delivered, attr.sender, attr.rcpt.address);
228 if (forward_dt == 0)
229 msg_panic("forward_append: missing forward_init call");
230
231 /*
232 * In order to find the recipient list, first index a table by
233 * delivered-to header address, then by envelope sender address.
234 */
235 if ((table_snd = (HTABLE *) htable_find(forward_dt, attr.delivered)) == 0) {
236 table_snd = htable_create(0);
237 htable_enter(forward_dt, attr.delivered, (void *) table_snd);
238 }
239 if ((info = (FORWARD_INFO *) htable_find(table_snd, attr.sender)) == 0) {
240 if ((info = forward_open(attr.request, attr.sender)) == 0)
241 return (-1);
242 htable_enter(table_snd, attr.sender, (void *) info);
243 }
244
245 /*
246 * Append the recipient to the message envelope. Don't send the original
247 * recipient or notification mask if it was reset due to mailing list
248 * expansion.
249 */
250 if (*attr.rcpt.dsn_orcpt)
251 rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%s",
252 MAIL_ATTR_DSN_ORCPT, attr.rcpt.dsn_orcpt);
253 if (attr.rcpt.dsn_notify)
254 rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%d",
255 MAIL_ATTR_DSN_NOTIFY, attr.rcpt.dsn_notify);
256 if (*attr.rcpt.orig_addr)
257 rec_fputs(info->cleanup, REC_TYPE_ORCP, attr.rcpt.orig_addr);
258 rec_fputs(info->cleanup, REC_TYPE_RCPT, attr.rcpt.address);
259
260 return (vstream_ferror(info->cleanup));
261 }
262
263 /* forward_send - send forwarded message */
264
forward_send(FORWARD_INFO * info,DELIVER_REQUEST * request,DELIVER_ATTR attr,char * delivered)265 static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
266 DELIVER_ATTR attr, char *delivered)
267 {
268 const char *myname = "forward_send";
269 VSTRING *buffer = vstring_alloc(100);
270 VSTRING *folded;
271 int status;
272 int rec_type = 0;
273
274 /*
275 * Start the message content segment. Prepend our Delivered-To: header to
276 * the message data. Stop at the first error. XXX Rely on the front-end
277 * services to enforce record size limits.
278 */
279 rec_fputs(info->cleanup, REC_TYPE_MESG, "");
280 vstring_strcpy(buffer, delivered);
281 rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)",
282 var_myhostname, var_mail_name);
283 rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s",
284 info->queue_id, mail_date(info->posting_time.tv_sec));
285 if (local_deliver_hdr_mask & DELIVER_HDR_FWD) {
286 folded = vstring_alloc(100);
287 rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s",
288 casefold(folded, (STR(buffer))));
289 vstring_free(folded);
290 }
291 if ((status = vstream_ferror(info->cleanup)) == 0)
292 if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
293 msg_fatal("%s: seek queue file %s: %m:",
294 myname, VSTREAM_PATH(attr.fp));
295 while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) {
296 if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM)
297 break;
298 status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type);
299 }
300 if (status == 0 && rec_type != REC_TYPE_XTRA) {
301 msg_warn("%s: bad record type: %d in message content",
302 info->queue_id, rec_type);
303 status |= mark_corrupt(attr.fp);
304 }
305
306 /*
307 * Send the end-of-data marker only when there were no errors.
308 */
309 if (status == 0) {
310 rec_fputs(info->cleanup, REC_TYPE_XTRA, "");
311 rec_fputs(info->cleanup, REC_TYPE_END, "");
312 }
313
314 /*
315 * Retrieve the cleanup service completion status only if there are no
316 * problems.
317 */
318 if (status == 0)
319 if (vstream_fflush(info->cleanup)
320 || attr_scan(info->cleanup, ATTR_FLAG_MISSING,
321 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
322 ATTR_TYPE_END) != 1)
323 status = 1;
324
325 /*
326 * Log successful forwarding.
327 *
328 * XXX DSN alias and .forward expansion already report SUCCESS, so don't do
329 * it again here.
330 */
331 if (status == 0) {
332 attr.rcpt.dsn_notify =
333 (attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ?
334 DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS);
335 dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
336 "forwarded as %s", info->queue_id);
337 status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr));
338 }
339
340 /*
341 * Cleanup.
342 */
343 vstring_free(buffer);
344 return (status);
345 }
346
347 /* forward_finish - complete message forwarding requests and clean up */
348
forward_finish(DELIVER_REQUEST * request,DELIVER_ATTR attr,int cancel)349 int forward_finish(DELIVER_REQUEST *request, DELIVER_ATTR attr, int cancel)
350 {
351 HTABLE_INFO **dt_list;
352 HTABLE_INFO **dt;
353 HTABLE_INFO **sn_list;
354 HTABLE_INFO **sn;
355 HTABLE *table_snd;
356 char *delivered;
357 char *sender;
358 FORWARD_INFO *info;
359 int status = cancel;
360
361 /*
362 * Sanity checks.
363 */
364 if (forward_dt == 0)
365 msg_panic("forward_finish: missing forward_init call");
366
367 /*
368 * Walk over all delivered-to header addresses and over each envelope
369 * sender address.
370 */
371 for (dt = dt_list = htable_list(forward_dt); *dt; dt++) {
372 delivered = dt[0]->key;
373 table_snd = (HTABLE *) dt[0]->value;
374 for (sn = sn_list = htable_list(table_snd); *sn; sn++) {
375 sender = sn[0]->key;
376 info = (FORWARD_INFO *) sn[0]->value;
377 if (status == 0)
378 status |= forward_send(info, request, attr, delivered);
379 if (msg_verbose)
380 msg_info("forward_finish: delivered %s sender %s status %d",
381 delivered, sender, status);
382 (void) vstream_fclose(info->cleanup);
383 myfree(info->queue_id);
384 myfree((void *) info);
385 }
386 myfree((void *) sn_list);
387 htable_free(table_snd, (void (*) (void *)) 0);
388 }
389 myfree((void *) dt_list);
390 htable_free(forward_dt, (void (*) (void *)) 0);
391 forward_dt = 0;
392 return (status);
393 }
394