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