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