1/*++
2/* NAME
3/*	abounce 3
4/* SUMMARY
5/*	asynchronous bounce/defer/trace service client
6/* SYNOPSIS
7/*	#include <abounce.h>
8/*
9/*	void	abounce_flush(flags, queue, id, encoding, sender,
10/*				dsn_envid, dsn_ret, callback, context)
11/*	int	flags;
12/*	const char *queue;
13/*	const char *id;
14/*	const char *encoding;
15/*	const char *sender;
16/*	const char *dsn_envid;
17/*	int	dsn_ret;
18/*	void	(*callback)(int status, char *context);
19/*	char	*context;
20/*
21/*	void	abounce_flush_verp(flags, queue, id, encoding, sender,
22/*				dsn_envid, dsn_ret, verp, callback, context)
23/*	int	flags;
24/*	const char *queue;
25/*	const char *id;
26/*	const char *encoding;
27/*	const char *sender;
28/*	const char *dsn_envid;
29/*	int	dsn_ret;
30/*	const char *verp;
31/*	void	(*callback)(int status, char *context);
32/*	char	*context;
33/*
34/*	void	adefer_flush(flags, queue, id, encoding, sender,
35/*				dsn_envid, dsn_ret, callback, context)
36/*	int	flags;
37/*	const char *queue;
38/*	const char *id;
39/*	const char *encoding;
40/*	const char *sender;
41/*	const char *dsn_envid;
42/*	int	dsn_ret;
43/*	void	(*callback)(int status, char *context);
44/*	char	*context;
45/*
46/*	void	adefer_flush_verp(flags, queue, id, encoding, sender,
47/*				dsn_envid, dsn_ret, verp, callback, context)
48/*	int	flags;
49/*	const char *queue;
50/*	const char *id;
51/*	const char *encoding;
52/*	const char *sender;
53/*	const char *dsn_envid;
54/*	int	dsn_ret;
55/*	const char *verp;
56/*	void	(*callback)(int status, char *context);
57/*	char	*context;
58/*
59/*	void	adefer_warn(flags, queue, id, encoding, sender,
60/*				dsn_envid, dsn_ret, callback, context)
61/*	int	flags;
62/*	const char *queue;
63/*	const char *id;
64/*	const char *encoding;
65/*	const char *sender;
66/*	const char *dsn_envid;
67/*	int	dsn_ret;
68/*	void	(*callback)(int status, char *context);
69/*	char	*context;
70/*
71/*	void	atrace_flush(flags, queue, id, encoding, sender,
72/*				dsn_envid, dsn_ret, callback, context)
73/*	int	flags;
74/*	const char *queue;
75/*	const char *id;
76/*	const char *encoding;
77/*	const char *sender;
78/*	const char *dsn_envid;
79/*	int	dsn_ret;
80/*	void	(*callback)(int status, char *context);
81/*	char	*context;
82/* DESCRIPTION
83/*	This module implements an asynchronous interface to the
84/*	bounce/defer/trace service for submitting sender notifications
85/*	without waiting for completion of the request.
86/*
87/*	abounce_flush() bounces the specified message to
88/*	the specified sender, including the bounce log that was
89/*	built with bounce_append().
90/*
91/*	abounce_flush_verp() is like abounce_flush() but sends
92/*	one VERP style notification per undeliverable recipient.
93/*
94/*	adefer_flush() bounces the specified message to
95/*	the specified sender, including the defer log that was
96/*	built with defer_append().
97/*	adefer_flush() requests that the deferred recipients are deleted
98/*	from the original queue file.
99/*
100/*	adefer_flush_verp() is like adefer_flush() but sends
101/*	one VERP style notification per undeliverable recipient.
102/*
103/*	adefer_warn() sends a "mail is delayed" notification to
104/*	the specified sender, including the defer log that was
105/*	built with defer_append().
106/*
107/*	atrace_flush() returns the specified message to the specified
108/*	sender, including the message delivery record log that was
109/*	built with vtrace_append().
110/*
111/*	Arguments:
112/* .IP flags
113/*	The bitwise OR of zero or more of the following (specify
114/*	BOUNCE_FLAG_NONE to request no special processing):
115/* .RS
116/* .IP BOUNCE_FLAG_CLEAN
117/*	Delete the bounce log in case of an error (as in: pretend
118/*	that we never even tried to bounce this message).
119/* .IP BOUNCE_FLAG_DELRCPT
120/*	When specified with a flush operation, request that
121/*	recipients be deleted from the queue file.
122/*
123/*	Note: the bounce daemon ignores this request when the
124/*	recipient queue file offset is <= 0.
125/* .IP BOUNCE_FLAG_COPY
126/*	Request that a postmaster copy is sent.
127/* .RE
128/* .IP queue
129/*	The message queue name of the original message file.
130/* .IP id
131/*	The message queue id if the original message file. The bounce log
132/*	file has the same name as the original message file.
133/* .IP encoding
134/*	The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
135/* .IP sender
136/*	The sender envelope address.
137/* .IP dsn_envid
138/*	Optional DSN envelope ID.
139/* .IP ret
140/*	Optional DSN return full/headers option.
141/* .IP verp
142/*	VERP delimiter characters.
143/* .IP callback
144/*	Name of a routine that receives the notification status as
145/*	documented for bounce_flush() or defer_flush().
146/* .IP context
147/*	Application-specific context that is passed through to the
148/*	callback routine. Use proper casts or the world will come
149/*	to an end.
150/* DIAGNOSTICS
151/*	In case of success, these functions log the action, and return a
152/*	zero result via the callback routine. Otherwise, the functions
153/*	return a non-zero result via the callback routine, and when
154/*	BOUNCE_FLAG_CLEAN is disabled, log that message delivery is deferred.
155/* LICENSE
156/* .ad
157/* .fi
158/*	The Secure Mailer license must be distributed with this software.
159/* AUTHOR(S)
160/*	Wietse Venema
161/*	IBM T.J. Watson Research
162/*	P.O. Box 704
163/*	Yorktown Heights, NY 10598, USA
164/*--*/
165
166/* System library. */
167
168#include <sys_defs.h>
169
170/* Utility library. */
171
172#include <msg.h>
173#include <mymalloc.h>
174#include <events.h>
175#include <vstream.h>
176
177/* Global library. */
178
179#include <mail_params.h>
180#include <mail_proto.h>
181#include <abounce.h>
182
183/* Application-specific. */
184
185 /*
186  * Each bounce/defer flush/warn request is implemented by sending the
187  * request to the bounce/defer server, and by creating a pseudo thread that
188  * suspends itself until the server replies (or dies). Upon wakeup, the
189  * pseudo thread delivers the request completion status to the application
190  * and destroys itself. The structure below maintains all the necessary
191  * request state while the pseudo thread is suspended.
192  */
193typedef struct {
194    int     command;			/* bounce request type */
195    int     flags;			/* bounce options */
196    char   *id;				/* queue ID for logging */
197    ABOUNCE_FN callback;		/* application callback */
198    char   *context;			/* application context */
199    VSTREAM *fp;			/* server I/O handle */
200} ABOUNCE;
201
202 /*
203  * Encapsulate common code.
204  */
205#define ABOUNCE_EVENT_ENABLE(fd, callback, context, timeout) do { \
206	event_enable_read((fd), (callback), (context)); \
207	event_request_timer((callback), (context), (timeout)); \
208    } while (0)
209
210#define ABOUNCE_EVENT_DISABLE(fd, callback, context) do { \
211	event_cancel_timer((callback), (context)); \
212	event_disable_readwrite(fd); \
213    } while (0)
214
215 /*
216  * If we set the reply timeout too short, then we make the problem worse by
217  * increasing overload. With 1000s timeout mail will keep flowing, but there
218  * will be a large number of blocked bounce processes, and some resource is
219  * likely to run out.
220  */
221#define ABOUNCE_TIMEOUT	1000
222
223/* abounce_done - deliver status to application and clean up pseudo thread */
224
225static void abounce_done(ABOUNCE *ap, int status)
226{
227    (void) vstream_fclose(ap->fp);
228    if (status != 0 && (ap->flags & BOUNCE_FLAG_CLEAN) == 0)
229	msg_info("%s: status=deferred (%s failed)", ap->id,
230		 ap->command == BOUNCE_CMD_FLUSH ? "bounce" :
231		 ap->command == BOUNCE_CMD_WARN ? "delay warning" :
232		 ap->command == BOUNCE_CMD_VERP ? "verp" :
233		 ap->command == BOUNCE_CMD_TRACE ? "trace" :
234		 "whatever");
235    ap->callback(status, ap->context);
236    myfree(ap->id);
237    myfree((char *) ap);
238}
239
240/* abounce_event - resume pseudo thread after server reply event */
241
242static void abounce_event(int event, char *context)
243{
244    ABOUNCE *ap = (ABOUNCE *) context;
245    int     status;
246
247    ABOUNCE_EVENT_DISABLE(vstream_fileno(ap->fp), abounce_event, context);
248    abounce_done(ap, (event != EVENT_TIME
249		      && attr_scan(ap->fp, ATTR_FLAG_STRICT,
250				   ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
251				   ATTR_TYPE_END) == 1) ? status : -1);
252}
253
254/* abounce_request_verp - suspend pseudo thread until server reply event */
255
256static void abounce_request_verp(const char *class, const char *service,
257				         int command, int flags,
258				         const char *queue, const char *id,
259				         const char *encoding,
260				         const char *sender,
261				         const char *dsn_envid,
262				         int dsn_ret,
263				         const char *verp,
264				         ABOUNCE_FN callback,
265				         char *context)
266{
267    ABOUNCE *ap;
268
269    /*
270     * Save pseudo thread state. Connect to the server. Send the request and
271     * suspend the pseudo thread until the server replies (or dies).
272     */
273    ap = (ABOUNCE *) mymalloc(sizeof(*ap));
274    ap->command = command;
275    ap->flags = flags;
276    ap->id = mystrdup(id);
277    ap->callback = callback;
278    ap->context = context;
279    ap->fp = mail_connect_wait(class, service);
280
281    if (attr_print(ap->fp, ATTR_FLAG_NONE,
282		   ATTR_TYPE_INT, MAIL_ATTR_NREQ, command,
283		   ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
284		   ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
285		   ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
286		   ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
287		   ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
288		   ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
289		   ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
290		   ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp,
291		   ATTR_TYPE_END) == 0
292	&& vstream_fflush(ap->fp) == 0) {
293	ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
294			     (char *) ap, ABOUNCE_TIMEOUT);
295    } else {
296	abounce_done(ap, -1);
297    }
298}
299
300/* abounce_flush_verp - asynchronous bounce flush */
301
302void    abounce_flush_verp(int flags, const char *queue, const char *id,
303			           const char *encoding, const char *sender,
304			           const char *dsn_envid, int dsn_ret,
305			           const char *verp, ABOUNCE_FN callback,
306			           char *context)
307{
308    abounce_request_verp(MAIL_CLASS_PRIVATE, var_bounce_service,
309			 BOUNCE_CMD_VERP, flags, queue, id, encoding,
310		       sender, dsn_envid, dsn_ret, verp, callback, context);
311}
312
313/* adefer_flush_verp - asynchronous defer flush */
314
315void    adefer_flush_verp(int flags, const char *queue, const char *id,
316			          const char *encoding, const char *sender,
317			          const char *dsn_envid, int dsn_ret,
318			          const char *verp, ABOUNCE_FN callback,
319			          char *context)
320{
321    flags |= BOUNCE_FLAG_DELRCPT;
322    abounce_request_verp(MAIL_CLASS_PRIVATE, var_defer_service,
323			 BOUNCE_CMD_VERP, flags, queue, id, encoding,
324		       sender, dsn_envid, dsn_ret, verp, callback, context);
325}
326
327/* abounce_request - suspend pseudo thread until server reply event */
328
329static void abounce_request(const char *class, const char *service,
330			            int command, int flags,
331			            const char *queue, const char *id,
332			            const char *encoding, const char *sender,
333			            const char *dsn_envid, int dsn_ret,
334			            ABOUNCE_FN callback, char *context)
335{
336    ABOUNCE *ap;
337
338    /*
339     * Save pseudo thread state. Connect to the server. Send the request and
340     * suspend the pseudo thread until the server replies (or dies).
341     */
342    ap = (ABOUNCE *) mymalloc(sizeof(*ap));
343    ap->command = command;
344    ap->flags = flags;
345    ap->id = mystrdup(id);
346    ap->callback = callback;
347    ap->context = context;
348    ap->fp = mail_connect_wait(class, service);
349
350    if (attr_print(ap->fp, ATTR_FLAG_NONE,
351		   ATTR_TYPE_INT, MAIL_ATTR_NREQ, command,
352		   ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
353		   ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
354		   ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
355		   ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
356		   ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
357		   ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
358		   ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
359		   ATTR_TYPE_END) == 0
360	&& vstream_fflush(ap->fp) == 0) {
361	ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
362			     (char *) ap, ABOUNCE_TIMEOUT);
363    } else {
364	abounce_done(ap, -1);
365    }
366}
367
368/* abounce_flush - asynchronous bounce flush */
369
370void    abounce_flush(int flags, const char *queue, const char *id,
371		              const char *encoding, const char *sender,
372		              const char *dsn_envid, int dsn_ret,
373		              ABOUNCE_FN callback, char *context)
374{
375    abounce_request(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH,
376		    flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
377		    callback, context);
378}
379
380/* adefer_flush - asynchronous defer flush */
381
382void    adefer_flush(int flags, const char *queue, const char *id,
383		             const char *encoding, const char *sender,
384		             const char *dsn_envid, int dsn_ret,
385		             ABOUNCE_FN callback, char *context)
386{
387    flags |= BOUNCE_FLAG_DELRCPT;
388    abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH,
389		    flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
390		    callback, context);
391}
392
393/* adefer_warn - send copy of defer log to sender as warning bounce */
394
395void    adefer_warn(int flags, const char *queue, const char *id,
396		            const char *encoding, const char *sender,
397		            const char *dsn_envid, int dsn_ret,
398		            ABOUNCE_FN callback, char *context)
399{
400    abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN,
401		    flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
402		    callback, context);
403}
404
405/* atrace_flush - asynchronous trace flush */
406
407void    atrace_flush(int flags, const char *queue, const char *id,
408		             const char *encoding, const char *sender,
409		             const char *dsn_envid, int dsn_ret,
410		             ABOUNCE_FN callback, char *context)
411{
412    abounce_request(MAIL_CLASS_PRIVATE, var_trace_service, BOUNCE_CMD_TRACE,
413		    flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
414		    callback, context);
415}
416