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