summaryrefslogtreecommitdiffstats
path: root/src/cleanup/cleanup_bounce.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cleanup/cleanup_bounce.c')
-rw-r--r--src/cleanup/cleanup_bounce.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/src/cleanup/cleanup_bounce.c b/src/cleanup/cleanup_bounce.c
new file mode 100644
index 0000000..361875e
--- /dev/null
+++ b/src/cleanup/cleanup_bounce.c
@@ -0,0 +1,257 @@
+/*++
+/* NAME
+/* cleanup_bounce 3
+/* SUMMARY
+/* bounce all recipients
+/* SYNOPSIS
+/* #include "cleanup.h"
+/*
+/* void cleanup_bounce(state)
+/* CLEANUP_STATE *state;
+/* DESCRIPTION
+/* cleanup_bounce() updates the bounce log on request by client
+/* programs that cannot handle such problems themselves.
+/*
+/* Upon successful completion, all error flags are reset,
+/* and the message is scheduled for deletion.
+/* Otherwise, the CLEANUP_STAT_WRITE error flag is raised.
+/*
+/* Arguments:
+/* .IP state
+/* Queue file and message processing state. This state is
+/* updated as records are processed and as errors happen.
+/* 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 <stringops.h>
+#include <stdlib.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <bounce.h>
+#include <dsn_util.h>
+#include <record.h>
+#include <rec_type.h>
+#include <dsn_mask.h>
+#include <mail_queue.h>
+#include <rec_attr_map.h>
+
+/* Application-specific. */
+
+#include "cleanup.h"
+
+#define STR(x) vstring_str(x)
+
+/* cleanup_bounce_append - update bounce logfile */
+
+static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
+ DSN *dsn)
+{
+ MSG_STATS stats;
+
+ /*
+ * Don't log a spurious warning (for example, when soft_bounce is turned
+ * on). bounce_append() already logs a record when the logfile can't be
+ * updated. Set the write error flag, so that a maildrop queue file won't
+ * be destroyed.
+ */
+ if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
+ CLEANUP_MSG_STATS(&stats, state),
+ rcpt, "none", dsn) != 0) {
+ state->errs |= CLEANUP_STAT_WRITE;
+ }
+}
+
+/* cleanup_bounce - bounce all recipients */
+
+int cleanup_bounce(CLEANUP_STATE *state)
+{
+ const char *myname = "cleanup_bounce";
+ VSTRING *buf = vstring_alloc(100);
+ const CLEANUP_STAT_DETAIL *detail;
+ DSN_SPLIT dp;
+ const char *dsn_status;
+ const char *dsn_text;
+ char *rcpt = 0;
+ RECIPIENT recipient;
+ DSN dsn;
+ char *attr_name;
+ char *attr_value;
+ char *dsn_orcpt = 0;
+ int dsn_notify = 0;
+ char *orig_rcpt = 0;
+ char *start;
+ int rec_type;
+ int junk;
+ long curr_offset;
+ const char *encoding;
+ const char *dsn_envid;
+ int dsn_ret;
+ int bounce_err;
+
+ /*
+ * Parse the failure reason if one was given, otherwise use a generic
+ * mapping from cleanup-internal error code to (DSN + text).
+ */
+ if (state->reason) {
+ dsn_split(&dp, "5.0.0", state->reason);
+ dsn_status = DSN_STATUS(dp.dsn);
+ dsn_text = dp.text;
+ } else {
+ detail = cleanup_stat_detail(state->errs);
+ dsn_status = detail->dsn;
+ dsn_text = detail->text;
+ }
+
+ /*
+ * Create a bounce logfile with one entry for each final recipient.
+ * Degrade gracefully in case of no recipients or no queue file.
+ *
+ * Victor Duchovni observes that the number of recipients in the queue file
+ * can potentially be very large due to virtual alias expansion. This can
+ * expand the recipient count by virtual_alias_expansion_limit (default:
+ * 1000) times.
+ *
+ * After a queue file write error, purge any unwritten data (so that
+ * vstream_fseek() won't fail while trying to flush it) and reset the
+ * stream error flags to avoid false alarms.
+ */
+ if (vstream_ferror(state->dst) || vstream_fflush(state->dst)) {
+ (void) vstream_fpurge(state->dst, VSTREAM_PURGE_BOTH);
+ vstream_clearerr(state->dst);
+ }
+ if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
+ msg_fatal("%s: seek %s: %m", myname, cleanup_path);
+
+ while ((state->errs & CLEANUP_STAT_WRITE) == 0) {
+ if ((curr_offset = vstream_ftell(state->dst)) < 0)
+ msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+ if ((rec_type = rec_get(state->dst, buf, 0)) <= 0
+ || rec_type == REC_TYPE_END)
+ break;
+ start = STR(buf);
+ if (rec_type == REC_TYPE_ATTR) {
+ if (split_nameval(STR(buf), &attr_name, &attr_value) != 0
+ || *attr_value == 0)
+ continue;
+ /* Map attribute names to pseudo record type. */
+ if ((junk = rec_attr_map(attr_name)) != 0) {
+ start = attr_value;
+ rec_type = junk;
+ }
+ }
+ switch (rec_type) {
+ case REC_TYPE_DSN_ORCPT: /* RCPT TO ORCPT parameter */
+ if (dsn_orcpt != 0) /* can't happen */
+ myfree(dsn_orcpt);
+ dsn_orcpt = mystrdup(start);
+ break;
+ case REC_TYPE_DSN_NOTIFY: /* RCPT TO NOTIFY parameter */
+ if (alldig(start) && (junk = atoi(start)) > 0
+ && DSN_NOTIFY_OK(junk))
+ dsn_notify = junk;
+ else
+ dsn_notify = 0;
+ break;
+ case REC_TYPE_ORCP: /* unmodified RCPT TO address */
+ if (orig_rcpt != 0) /* can't happen */
+ myfree(orig_rcpt);
+ orig_rcpt = mystrdup(start);
+ break;
+ case REC_TYPE_RCPT: /* rewritten RCPT TO address */
+ rcpt = start;
+ RECIPIENT_ASSIGN(&recipient, curr_offset,
+ dsn_orcpt ? dsn_orcpt : "", dsn_notify,
+ orig_rcpt ? orig_rcpt : rcpt, rcpt);
+ (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
+ cleanup_bounce_append(state, &recipient, &dsn);
+ /* FALLTHROUGH */
+ case REC_TYPE_DRCP: /* canceled recipient */
+ case REC_TYPE_DONE: /* can't happen */
+ if (orig_rcpt != 0) {
+ myfree(orig_rcpt);
+ orig_rcpt = 0;
+ }
+ if (dsn_orcpt != 0) {
+ myfree(dsn_orcpt);
+ dsn_orcpt = 0;
+ }
+ dsn_notify = 0;
+ break;
+ }
+ }
+ if (orig_rcpt != 0) /* can't happen */
+ myfree(orig_rcpt);
+ if (dsn_orcpt != 0) /* can't happen */
+ myfree(dsn_orcpt);
+
+ /*
+ * No recipients. Yes, this can happen.
+ */
+ if ((state->errs & CLEANUP_STAT_WRITE) == 0 && rcpt == 0) {
+ RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown");
+ (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
+ cleanup_bounce_append(state, &recipient, &dsn);
+ }
+ vstring_free(buf);
+
+ /*
+ * Flush the bounce logfile to the sender. See also qmgr_active.c.
+ */
+ if ((state->errs & CLEANUP_STAT_WRITE) == 0) {
+ if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) == 0)
+ encoding = MAIL_ATTR_ENC_NONE;
+ dsn_envid = state->dsn_envid ?
+ state->dsn_envid : "";
+ /* Do not send unfiltered (body) content. */
+ dsn_ret = (state->errs & (CLEANUP_STAT_CONT | CLEANUP_STAT_SIZE)) ?
+ DSN_RET_HDRS : state->dsn_ret;
+
+ if (state->verp_delims == 0 || var_verp_bounce_off) {
+ bounce_err =
+ bounce_flush(BOUNCE_FLAG_CLEAN,
+ state->queue_name, state->queue_id,
+ encoding, state->smtputf8, state->sender,
+ dsn_envid, dsn_ret);
+ } else {
+ bounce_err =
+ bounce_flush_verp(BOUNCE_FLAG_CLEAN,
+ state->queue_name, state->queue_id,
+ encoding, state->smtputf8, state->sender,
+ dsn_envid, dsn_ret, state->verp_delims);
+ }
+ if (bounce_err != 0) {
+ msg_warn("%s: bounce message failure", state->queue_id);
+ state->errs |= CLEANUP_STAT_WRITE;
+ }
+ }
+
+ /*
+ * Schedule this message (and trace logfile) for deletion when all is
+ * well. When all is not well these files would be deleted too, but the
+ * client would get a different completion status so we have to carefully
+ * maintain the bits anyway.
+ */
+ if ((state->errs &= CLEANUP_STAT_WRITE) == 0)
+ state->flags |= CLEANUP_FLAG_DISCARD;
+
+ return (state->errs);
+}