13 /* int forward_append(attr)
16 /* int forward_finish(request, attr, cancel)
17 /* DELIVER_REQUEST *request;
21 /* This module implements the client interface for message
24 /* forward_init() initializes internal data structures.
26 /* forward_append() appends a recipient to the list of recipients
27 /* that will receive a message with the specified message sender
28 /* and delivered-to addresses.
30 /* forward_finish() forwards the actual message contents and
31 /* releases the memory allocated by forward_init() and by
32 /* forward_append(). When the \fIcancel\fR argument is true, no
33 /* messages will be forwarded. The \fIattr\fR argument specifies
34 /* the original message delivery attributes as they were before
35 /* alias or forward expansions.
37 /* A non-zero result means that the requested operation should
39 /* Warnings: problems connecting to the forwarding service,
40 /* corrupt message file. A corrupt message is saved to the
41 /* "corrupt" queue for further inspection.
42 /* Fatal: out of memory.
43 /* Panic: missing forward_init() or forward_finish() call.
47 /* The Secure Mailer license must be distributed with this software.
50 /* IBM T.J. Watson Research
52 /* Yorktown Heights, NY 10598, USA
61 /* Utility library. */
69 #include <vstring_vstream.h>
71 #include <stringops.h>
75 #include <mail_proto.h>
76 #include <cleanup_user.h>
80 #include <mark_corrupt.h>
81 #include <mail_date.h>
82 #include <mail_params.h>
85 /* Application-specific. */
90 * Use one cleanup service connection for each (delivered to, sender) pair.
92 static HTABLE
*forward_dt
;
94 typedef struct FORWARD_INFO
{
95 VSTREAM
*cleanup
; /* clean up service handle */
96 char *queue_id
; /* forwarded message queue id */
97 struct timeval posting_time
; /* posting time */
100 /* forward_init - prepare for forwarding */
102 int forward_init(void)
109 msg_panic("forward_init: missing forward_finish call");
111 forward_dt
= htable_create(0);
115 /* forward_open - open connection to cleanup service */
117 static FORWARD_INFO
*forward_open(DELIVER_REQUEST
*request
, const char *sender
)
119 VSTRING
*buffer
= vstring_alloc(100);
124 * Contact the cleanup service and save the new mail queue id. Request
125 * that the cleanup service bounces bad messages to the sender so that we
126 * can avoid the trouble of bounce management.
128 * In case you wonder what kind of bounces, examples are "too many hops",
129 * "message too large", perhaps some others. The reason not to bounce
130 * ourselves is that we don't really know who the recipients are.
132 cleanup
= mail_connect(MAIL_CLASS_PUBLIC
, var_cleanup_service
, BLOCKING
);
135 close_on_exec(vstream_fileno(cleanup
), CLOSE_ON_EXEC
);
136 if (attr_scan(cleanup
, ATTR_FLAG_STRICT
,
137 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, buffer
,
138 ATTR_TYPE_END
) != 1) {
139 vstream_fclose(cleanup
);
142 info
= (FORWARD_INFO
*) mymalloc(sizeof(FORWARD_INFO
));
143 info
->cleanup
= cleanup
;
144 info
->queue_id
= mystrdup(STR(buffer
));
145 GETTIMEOFDAY(&info
->posting_time
);
147 #define FORWARD_CLEANUP_FLAGS (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL)
149 attr_print(cleanup
, ATTR_FLAG_NONE
,
150 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, FORWARD_CLEANUP_FLAGS
,
154 * Send initial message envelope information. For bounces, set the
155 * designated sender: mailing list owner, posting user, whatever.
157 rec_fprintf(cleanup
, REC_TYPE_TIME
, REC_TYPE_TIME_FORMAT
,
158 REC_TYPE_TIME_ARG(info
->posting_time
));
159 rec_fputs(cleanup
, REC_TYPE_FROM
, sender
);
162 * Don't send the original envelope ID or full/headers return mask if it
163 * was reset due to mailing list expansion.
165 if (request
->dsn_ret
)
166 rec_fprintf(cleanup
, REC_TYPE_ATTR
, "%s=%d",
167 MAIL_ATTR_DSN_RET
, request
->dsn_ret
);
168 if (request
->dsn_envid
&& *(request
->dsn_envid
))
169 rec_fprintf(cleanup
, REC_TYPE_ATTR
, "%s=%s",
170 MAIL_ATTR_DSN_ENVID
, request
->dsn_envid
);
173 * Zero-length attribute values are place holders for unavailable
174 * attribute values. See qmgr_message.c. They are not meant to be
175 * propagated to queue files.
177 #define PASS_ATTR(fp, name, value) do { \
178 if ((value) && *(value)) \
179 rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \
182 PASS_ATTR(cleanup
, MAIL_ATTR_LOG_CLIENT_NAME
, request
->client_name
);
183 PASS_ATTR(cleanup
, MAIL_ATTR_LOG_CLIENT_ADDR
, request
->client_addr
);
184 PASS_ATTR(cleanup
, MAIL_ATTR_LOG_PROTO_NAME
, request
->client_proto
);
185 PASS_ATTR(cleanup
, MAIL_ATTR_LOG_HELO_NAME
, request
->client_helo
);
186 PASS_ATTR(cleanup
, MAIL_ATTR_SASL_METHOD
, request
->sasl_method
);
187 PASS_ATTR(cleanup
, MAIL_ATTR_SASL_USERNAME
, request
->sasl_username
);
188 PASS_ATTR(cleanup
, MAIL_ATTR_SASL_SENDER
, request
->sasl_sender
);
189 PASS_ATTR(cleanup
, MAIL_ATTR_RWR_CONTEXT
, request
->rewrite_context
);
191 vstring_free(buffer
);
195 /* forward_append - append recipient to message envelope */
197 int forward_append(DELIVER_ATTR attr
)
206 msg_info("forward delivered=%s sender=%s recip=%s",
207 attr
.delivered
, attr
.sender
, attr
.rcpt
.address
);
209 msg_panic("forward_append: missing forward_init call");
212 * In order to find the recipient list, first index a table by
213 * delivered-to header address, then by envelope sender address.
215 if ((table_snd
= (HTABLE
*) htable_find(forward_dt
, attr
.delivered
)) == 0) {
216 table_snd
= htable_create(0);
217 htable_enter(forward_dt
, attr
.delivered
, (char *) table_snd
);
219 if ((info
= (FORWARD_INFO
*) htable_find(table_snd
, attr
.sender
)) == 0) {
220 if ((info
= forward_open(attr
.request
, attr
.sender
)) == 0)
222 htable_enter(table_snd
, attr
.sender
, (char *) info
);
226 * Append the recipient to the message envelope. Don't send the original
227 * recipient or notification mask if it was reset due to mailing list
230 if (*attr
.rcpt
.dsn_orcpt
)
231 rec_fprintf(info
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
232 MAIL_ATTR_DSN_ORCPT
, attr
.rcpt
.dsn_orcpt
);
233 if (attr
.rcpt
.dsn_notify
)
234 rec_fprintf(info
->cleanup
, REC_TYPE_ATTR
, "%s=%d",
235 MAIL_ATTR_DSN_NOTIFY
, attr
.rcpt
.dsn_notify
);
236 if (*attr
.rcpt
.orig_addr
)
237 rec_fputs(info
->cleanup
, REC_TYPE_ORCP
, attr
.rcpt
.orig_addr
);
238 rec_fputs(info
->cleanup
, REC_TYPE_RCPT
, attr
.rcpt
.address
);
240 return (vstream_ferror(info
->cleanup
));
243 /* forward_send - send forwarded message */
245 static int forward_send(FORWARD_INFO
*info
, DELIVER_REQUEST
*request
,
246 DELIVER_ATTR attr
, char *delivered
)
248 const char *myname
= "forward_send";
249 VSTRING
*buffer
= vstring_alloc(100);
254 * Start the message content segment. Prepend our Delivered-To: header to
255 * the message data. Stop at the first error. XXX Rely on the front-end
256 * services to enforce record size limits.
258 rec_fputs(info
->cleanup
, REC_TYPE_MESG
, "");
259 vstring_strcpy(buffer
, delivered
);
260 rec_fprintf(info
->cleanup
, REC_TYPE_NORM
, "Received: by %s (%s)",
261 var_myhostname
, var_mail_name
);
262 rec_fprintf(info
->cleanup
, REC_TYPE_NORM
, "\tid %s; %s",
263 info
->queue_id
, mail_date(info
->posting_time
.tv_sec
));
264 if (local_deliver_hdr_mask
& DELIVER_HDR_FWD
)
265 rec_fprintf(info
->cleanup
, REC_TYPE_NORM
, "Delivered-To: %s",
266 lowercase(STR(buffer
)));
267 if ((status
= vstream_ferror(info
->cleanup
)) == 0)
268 if (vstream_fseek(attr
.fp
, attr
.offset
, SEEK_SET
) < 0)
269 msg_fatal("%s: seek queue file %s: %m:",
270 myname
, VSTREAM_PATH(attr
.fp
));
271 while (status
== 0 && (rec_type
= rec_get(attr
.fp
, buffer
, 0)) > 0) {
272 if (rec_type
!= REC_TYPE_CONT
&& rec_type
!= REC_TYPE_NORM
)
274 status
= (REC_PUT_BUF(info
->cleanup
, rec_type
, buffer
) != rec_type
);
276 if (status
== 0 && rec_type
!= REC_TYPE_XTRA
) {
277 msg_warn("%s: bad record type: %d in message content",
278 info
->queue_id
, rec_type
);
279 status
|= mark_corrupt(attr
.fp
);
283 * Send the end-of-data marker only when there were no errors.
286 rec_fputs(info
->cleanup
, REC_TYPE_XTRA
, "");
287 rec_fputs(info
->cleanup
, REC_TYPE_END
, "");
291 * Retrieve the cleanup service completion status only if there are no
295 if (vstream_fflush(info
->cleanup
)
296 || attr_scan(info
->cleanup
, ATTR_FLAG_MISSING
,
297 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, &status
,
302 * Log successful forwarding.
304 * XXX DSN alias and .forward expansion already report SUCCESS, so don't do
308 attr
.rcpt
.dsn_notify
=
309 (attr
.rcpt
.dsn_notify
== DSN_NOTIFY_SUCCESS
?
310 DSN_NOTIFY_NEVER
: attr
.rcpt
.dsn_notify
& ~DSN_NOTIFY_SUCCESS
);
311 dsb_update(attr
.why
, "2.0.0", "relayed", DSB_SKIP_RMTA
, DSB_SKIP_REPLY
,
312 "forwarded as %s", info
->queue_id
);
313 status
= sent(BOUNCE_FLAGS(request
), SENT_ATTR(attr
));
319 vstring_free(buffer
);
323 /* forward_finish - complete message forwarding requests and clean up */
325 int forward_finish(DELIVER_REQUEST
*request
, DELIVER_ATTR attr
, int cancel
)
327 HTABLE_INFO
**dt_list
;
329 HTABLE_INFO
**sn_list
;
341 msg_panic("forward_finish: missing forward_init call");
344 * Walk over all delivered-to header addresses and over each envelope
347 for (dt
= dt_list
= htable_list(forward_dt
); *dt
; dt
++) {
348 delivered
= dt
[0]->key
;
349 table_snd
= (HTABLE
*) dt
[0]->value
;
350 for (sn
= sn_list
= htable_list(table_snd
); *sn
; sn
++) {
352 info
= (FORWARD_INFO
*) sn
[0]->value
;
354 status
|= forward_send(info
, request
, attr
, delivered
);
356 msg_info("forward_finish: delivered %s sender %s status %d",
357 delivered
, sender
, status
);
358 (void) vstream_fclose(info
->cleanup
);
359 myfree(info
->queue_id
);
360 myfree((char *) info
);
362 myfree((char *) sn_list
);
363 htable_free(table_snd
, (void (*) (char *)) 0);
365 myfree((char *) dt_list
);
366 htable_free(forward_dt
, (void (*) (char *)) 0);