summaryrefslogtreecommitdiffstats
path: root/src/global/abounce.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/global/abounce.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/src/global/abounce.c b/src/global/abounce.c
new file mode 100644
index 0000000..efbf105
--- /dev/null
+++ b/src/global/abounce.c
@@ -0,0 +1,429 @@
+/*++
+/* NAME
+/* abounce 3
+/* SUMMARY
+/* asynchronous bounce/defer/trace service client
+/* SYNOPSIS
+/* #include <abounce.h>
+/*
+/* void abounce_flush(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void abounce_flush_verp(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, verp, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* const char *verp;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void adefer_flush(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void adefer_flush_verp(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, verp, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* const char *verp;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void adefer_warn(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/*
+/* void atrace_flush(flags, queue, id, encoding, smtputf8, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* int smtputf8;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, void *context);
+/* void *context;
+/* DESCRIPTION
+/* This module implements an asynchronous interface to the
+/* bounce/defer/trace service for submitting sender notifications
+/* without waiting for completion of the request.
+/*
+/* abounce_flush() bounces the specified message to
+/* the specified sender, including the bounce log that was
+/* built with bounce_append().
+/*
+/* abounce_flush_verp() is like abounce_flush() but sends
+/* one VERP style notification per undeliverable recipient.
+/*
+/* adefer_flush() bounces the specified message to
+/* the specified sender, including the defer log that was
+/* built with defer_append().
+/* adefer_flush() requests that the deferred recipients are deleted
+/* from the original queue file.
+/*
+/* adefer_flush_verp() is like adefer_flush() but sends
+/* one VERP style notification per undeliverable recipient.
+/*
+/* adefer_warn() sends a "mail is delayed" notification to
+/* the specified sender, including the defer log that was
+/* built with defer_append().
+/*
+/* atrace_flush() returns the specified message to the specified
+/* sender, including the message delivery record log that was
+/* built with vtrace_append().
+/*
+/* Arguments:
+/* .IP flags
+/* The bitwise OR of zero or more of the following (specify
+/* BOUNCE_FLAG_NONE to request no special processing):
+/* .RS
+/* .IP BOUNCE_FLAG_CLEAN
+/* Delete the bounce log in case of an error (as in: pretend
+/* that we never even tried to bounce this message).
+/* .IP BOUNCE_FLAG_DELRCPT
+/* When specified with a flush operation, request that
+/* recipients be deleted from the queue file.
+/*
+/* Note: the bounce daemon ignores this request when the
+/* recipient queue file offset is <= 0.
+/* .IP BOUNCE_FLAG_COPY
+/* Request that a postmaster copy is sent.
+/* .RE
+/* .IP queue
+/* The message queue name of the original message file.
+/* .IP id
+/* The message queue id if the original message file. The bounce log
+/* file has the same name as the original message file.
+/* .IP encoding
+/* The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
+/* .IP smtputf8
+/* The level of SMTPUTF8 support (to be defined).
+/* .IP sender
+/* The sender envelope address.
+/* .IP dsn_envid
+/* Optional DSN envelope ID.
+/* .IP ret
+/* Optional DSN return full/headers option.
+/* .IP verp
+/* VERP delimiter characters.
+/* .IP callback
+/* Name of a routine that receives the notification status as
+/* documented for bounce_flush() or defer_flush().
+/* .IP context
+/* Application-specific context that is passed through to the
+/* callback routine. Use proper casts or the world will come
+/* to an end.
+/* DIAGNOSTICS
+/* In case of success, these functions log the action, and return a
+/* zero result via the callback routine. Otherwise, the functions
+/* return a non-zero result via the callback routine, and when
+/* BOUNCE_FLAG_CLEAN is disabled, log that message delivery is deferred.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <abounce.h>
+
+/* Application-specific. */
+
+ /*
+ * Each bounce/defer flush/warn request is implemented by sending the
+ * request to the bounce/defer server, and by creating a pseudo thread that
+ * suspends itself until the server replies (or dies). Upon wakeup, the
+ * pseudo thread delivers the request completion status to the application
+ * and destroys itself. The structure below maintains all the necessary
+ * request state while the pseudo thread is suspended.
+ */
+typedef struct {
+ int command; /* bounce request type */
+ int flags; /* bounce options */
+ char *id; /* queue ID for logging */
+ ABOUNCE_FN callback; /* application callback */
+ void *context; /* application context */
+ VSTREAM *fp; /* server I/O handle */
+} ABOUNCE;
+
+ /*
+ * Encapsulate common code.
+ */
+#define ABOUNCE_EVENT_ENABLE(fd, callback, context, timeout) do { \
+ event_enable_read((fd), (callback), (context)); \
+ event_request_timer((callback), (context), (timeout)); \
+ } while (0)
+
+#define ABOUNCE_EVENT_DISABLE(fd, callback, context) do { \
+ event_cancel_timer((callback), (context)); \
+ event_disable_readwrite(fd); \
+ } while (0)
+
+ /*
+ * If we set the reply timeout too short, then we make the problem worse by
+ * increasing overload. With 1000s timeout mail will keep flowing, but there
+ * will be a large number of blocked bounce processes, and some resource is
+ * likely to run out.
+ */
+#define ABOUNCE_TIMEOUT 1000
+
+/* abounce_done - deliver status to application and clean up pseudo thread */
+
+static void abounce_done(ABOUNCE *ap, int status)
+{
+ (void) vstream_fclose(ap->fp);
+ if (status != 0 && (ap->flags & BOUNCE_FLAG_CLEAN) == 0)
+ msg_info("%s: status=deferred (%s failed)", ap->id,
+ ap->command == BOUNCE_CMD_FLUSH ? "bounce" :
+ ap->command == BOUNCE_CMD_WARN ? "delay warning" :
+ ap->command == BOUNCE_CMD_VERP ? "verp" :
+ ap->command == BOUNCE_CMD_TRACE ? "trace" :
+ "whatever");
+ ap->callback(status, ap->context);
+ myfree(ap->id);
+ myfree((void *) ap);
+}
+
+/* abounce_event - resume pseudo thread after server reply event */
+
+static void abounce_event(int event, void *context)
+{
+ ABOUNCE *ap = (ABOUNCE *) context;
+ int status;
+
+ ABOUNCE_EVENT_DISABLE(vstream_fileno(ap->fp), abounce_event, context);
+ abounce_done(ap, (event != EVENT_TIME
+ && attr_scan(ap->fp, ATTR_FLAG_STRICT,
+ RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
+ ATTR_TYPE_END) == 1) ? status : -1);
+}
+
+/* abounce_request_verp - suspend pseudo thread until server reply event */
+
+static void abounce_request_verp(const char *class, const char *service,
+ int command, int flags,
+ const char *queue, const char *id,
+ const char *encoding,
+ int smtputf8,
+ const char *sender,
+ const char *dsn_envid,
+ int dsn_ret,
+ const char *verp,
+ ABOUNCE_FN callback,
+ void *context)
+{
+ ABOUNCE *ap;
+
+ /*
+ * Save pseudo thread state. Connect to the server. Send the request and
+ * suspend the pseudo thread until the server replies (or dies).
+ */
+ ap = (ABOUNCE *) mymalloc(sizeof(*ap));
+ ap->command = command;
+ ap->flags = flags;
+ ap->id = mystrdup(id);
+ ap->callback = callback;
+ ap->context = context;
+ ap->fp = mail_connect_wait(class, service);
+
+ if (attr_print(ap->fp, ATTR_FLAG_NONE,
+ SEND_ATTR_INT(MAIL_ATTR_NREQ, command),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
+ SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
+ SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
+ SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
+ SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
+ SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
+ SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
+ SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
+ SEND_ATTR_STR(MAIL_ATTR_VERPDL, verp),
+ ATTR_TYPE_END) == 0
+ && vstream_fflush(ap->fp) == 0) {
+ ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
+ (void *) ap, ABOUNCE_TIMEOUT);
+ } else {
+ abounce_done(ap, -1);
+ }
+}
+
+/* abounce_flush_verp - asynchronous bounce flush */
+
+void abounce_flush_verp(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, const char *verp,
+ ABOUNCE_FN callback,
+ void *context)
+{
+ abounce_request_verp(MAIL_CLASS_PRIVATE, var_bounce_service,
+ BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
+ sender, dsn_envid, dsn_ret, verp, callback, context);
+}
+
+/* adefer_flush_verp - asynchronous defer flush */
+
+void adefer_flush_verp(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, const char *verp,
+ ABOUNCE_FN callback, void *context)
+{
+ flags |= BOUNCE_FLAG_DELRCPT;
+ abounce_request_verp(MAIL_CLASS_PRIVATE, var_defer_service,
+ BOUNCE_CMD_VERP, flags, queue, id, encoding, smtputf8,
+ sender, dsn_envid, dsn_ret, verp, callback, context);
+}
+
+/* abounce_request - suspend pseudo thread until server reply event */
+
+static void abounce_request(const char *class, const char *service,
+ int command, int flags,
+ const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender,
+ const char *dsn_envid, int dsn_ret,
+ ABOUNCE_FN callback, void *context)
+{
+ ABOUNCE *ap;
+
+ /*
+ * Save pseudo thread state. Connect to the server. Send the request and
+ * suspend the pseudo thread until the server replies (or dies).
+ */
+ ap = (ABOUNCE *) mymalloc(sizeof(*ap));
+ ap->command = command;
+ ap->flags = flags;
+ ap->id = mystrdup(id);
+ ap->callback = callback;
+ ap->context = context;
+ ap->fp = mail_connect_wait(class, service);
+
+ if (attr_print(ap->fp, ATTR_FLAG_NONE,
+ SEND_ATTR_INT(MAIL_ATTR_NREQ, command),
+ SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags),
+ SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
+ SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
+ SEND_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
+ SEND_ATTR_INT(MAIL_ATTR_SMTPUTF8, smtputf8),
+ SEND_ATTR_STR(MAIL_ATTR_SENDER, sender),
+ SEND_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
+ SEND_ATTR_INT(MAIL_ATTR_DSN_RET, dsn_ret),
+ ATTR_TYPE_END) == 0
+ && vstream_fflush(ap->fp) == 0) {
+ ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
+ (void *) ap, ABOUNCE_TIMEOUT);
+ } else {
+ abounce_done(ap, -1);
+ }
+}
+
+/* abounce_flush - asynchronous bounce flush */
+
+void abounce_flush(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback,
+ void *context)
+{
+ abounce_request(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, callback, context);
+}
+
+/* adefer_flush - asynchronous defer flush */
+
+void adefer_flush(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback, void *context)
+{
+ flags |= BOUNCE_FLAG_DELRCPT;
+ abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, callback, context);
+}
+
+/* adefer_warn - send copy of defer log to sender as warning bounce */
+
+void adefer_warn(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback, void *context)
+{
+ abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, callback, context);
+}
+
+/* atrace_flush - asynchronous trace flush */
+
+void atrace_flush(int flags, const char *queue, const char *id,
+ const char *encoding, int smtputf8,
+ const char *sender, const char *dsn_envid,
+ int dsn_ret, ABOUNCE_FN callback, void *context)
+{
+ abounce_request(MAIL_CLASS_PRIVATE, var_trace_service, BOUNCE_CMD_TRACE,
+ flags, queue, id, encoding, smtputf8, sender, dsn_envid,
+ dsn_ret, callback, context);
+}