summaryrefslogtreecommitdiffstats
path: root/src/smtp/smtp_rcpt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/smtp/smtp_rcpt.c')
-rw-r--r--src/smtp/smtp_rcpt.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/smtp/smtp_rcpt.c b/src/smtp/smtp_rcpt.c
new file mode 100644
index 0000000..6608ea8
--- /dev/null
+++ b/src/smtp/smtp_rcpt.c
@@ -0,0 +1,226 @@
+/*++
+/* NAME
+/* smtp_rcpt 3
+/* SUMMARY
+/* application-specific recipient list operations
+/* SYNOPSIS
+/* #include <smtp.h>
+/*
+/* SMTP_RCPT_INIT(state)
+/* SMTP_STATE *state;
+/*
+/* SMTP_RCPT_DROP(state, rcpt)
+/* SMTP_STATE *state;
+/* RECIPIENT *rcpt;
+/*
+/* SMTP_RCPT_KEEP(state, rcpt)
+/* SMTP_STATE *state;
+/* RECIPIENT *rcpt;
+/*
+/* SMTP_RCPT_ISMARKED(rcpt)
+/* RECIPIENT *rcpt;
+/*
+/* void smtp_rcpt_cleanup(SMTP_STATE *state)
+/* SMTP_STATE *state;
+/*
+/* int SMTP_RCPT_LEFT(state)
+/* SMTP_STATE *state;
+/*
+/* int SMTP_RCPT_MARK_COUNT(state)
+/* SMTP_STATE *state;
+/*
+/* void smtp_rcpt_done(state, resp, rcpt)
+/* SMTP_STATE *state;
+/* SMTP_RESP *resp;
+/* RECIPIENT *rcpt;
+/* DESCRIPTION
+/* This module implements application-specific mark and sweep
+/* operations on recipient lists. Operation is as follows:
+/* .IP \(bu
+/* In the course of a delivery attempt each recipient is
+/* marked either as DROP (remove from recipient list) or KEEP
+/* (deliver to alternate mail server).
+/* .IP \(bu
+/* After a delivery attempt any recipients marked DROP are deleted
+/* from the request, and the left-over recipients are unmarked.
+/* .PP
+/* The mark/sweep algorithm is implemented in a redundant manner,
+/* and ensures that all recipients are explicitly accounted for.
+/*
+/* Operations with upper case names are implemented by macros
+/* whose arguments may be evaluated more than once.
+/*
+/* SMTP_RCPT_INIT() initializes application-specific recipient
+/* information and must be called before the first delivery attempt.
+/*
+/* SMTP_RCPT_DROP() marks the specified recipient as DROP (remove
+/* from recipient list). It is an error to mark an already marked
+/* recipient.
+/*
+/* SMTP_RCPT_KEEP() marks the specified recipient as KEEP (deliver
+/* to alternate mail server). It is an error to mark an already
+/* marked recipient.
+/*
+/* SMTP_RCPT_ISMARKED() returns non-zero when the specified
+/* recipient is marked.
+/*
+/* SMTP_RCPT_LEFT() returns the number of left_over recipients
+/* (the total number of marked and non-marked recipients).
+/*
+/* SMTP_RCPT_MARK_COUNT() returns the number of left_over
+/* recipients that are marked.
+/*
+/* smtp_rcpt_cleanup() cleans up the in-memory recipient list.
+/* It removes the recipients marked DROP from the left-over
+/* recipients, unmarks the left-over recipients, and enforces
+/* the requirement that all recipients are marked upon entry.
+/*
+/* smtp_rcpt_done() logs that a recipient is completed and upon
+/* success it marks the recipient as done in the queue file.
+/* Finally, it marks the in-memory recipient as DROP.
+/*
+/* Note: smtp_rcpt_done() may change the order of the recipient
+/* list.
+/* DIAGNOSTICS
+/* Panic: interface violation.
+/*
+/* When a recipient can't be logged as completed, the recipient is
+/* logged as deferred instead.
+/* BUGS
+/* The single recipient list abstraction dates from the time
+/* that the SMTP client would give up after one SMTP session,
+/* so that each recipient was either bounced, delivered or
+/* deferred. Implicitly, all recipients were marked as DROP.
+/*
+/* This abstraction is less convenient when an SMTP client
+/* must be able to deliver left-over recipients to a backup
+/* host. It might be more natural to have an input list with
+/* recipients to deliver, and an output list with left-over
+/* recipients.
+/* 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
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h> /* smtp_rcpt_cleanup */
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <deliver_request.h> /* smtp_rcpt_done */
+#include <deliver_completed.h> /* smtp_rcpt_done */
+#include <sent.h> /* smtp_rcpt_done */
+#include <dsn_mask.h> /* smtp_rcpt_done */
+
+/* Application-specific. */
+
+#include <smtp.h>
+
+/* smtp_rcpt_done - mark recipient as done or else */
+
+void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
+{
+ DELIVER_REQUEST *request = state->request;
+ SMTP_SESSION *session = state->session;
+ SMTP_ITERATOR *iter = state->iterator;
+ DSN_BUF *why = state->why;
+ const char *dsn_action = "relayed";
+ int status;
+
+ /*
+ * Assume this was intermediate delivery when the server announced DSN
+ * support, and don't send a DSN "SUCCESS" notification.
+ */
+ if (session->features & SMTP_FEATURE_DSN)
+ rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;
+
+ /*
+ * Assume this was final delivery when the LMTP server announced no DSN
+ * support. In backwards compatibility mode, send a "relayed" instead of
+ * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify"
+ * the expression. The redundancy is for clarity. It is trivially
+ * eliminated by the compiler. There is no need to sacrifice clarity for
+ * the sake of "performance".
+ */
+ if ((session->features & SMTP_FEATURE_DSN) == 0
+ && !smtp_mode
+ && (smtp_cli_attr.flags & SMTP_CLI_FLAG_FINAL_DELIVERY) != 0)
+ dsn_action = "delivered";
+
+ /*
+ * Report success and delete the recipient from the delivery request.
+ * Defer if the success can't be reported.
+ *
+ * Note: the DSN action is ignored in case of address probes.
+ */
+ dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host),
+ DSB_DTYPE_SMTP, resp->str, "%s", resp->str);
+
+ status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
+ request->queue_id, &request->msg_stats, rcpt,
+ session->namaddrport, DSN_FROM_DSN_BUF(why));
+ if (status == 0)
+ if (request->flags & DEL_REQ_FLAG_SUCCESS)
+ deliver_completed(state->src, rcpt->offset);
+ SMTP_RCPT_DROP(state, rcpt);
+ state->status |= status;
+}
+
+/* smtp_rcpt_cleanup_callback - qsort callback */
+
+static int smtp_rcpt_cleanup_callback(const void *a, const void *b)
+{
+ return (((RECIPIENT *) a)->u.status - ((RECIPIENT *) b)->u.status);
+}
+
+/* smtp_rcpt_cleanup - purge completed recipients from request */
+
+void smtp_rcpt_cleanup(SMTP_STATE *state)
+{
+ RECIPIENT_LIST *rcpt_list = &state->request->rcpt_list;
+ RECIPIENT *rcpt;
+
+ /*
+ * Sanity checks.
+ */
+ if (state->rcpt_drop + state->rcpt_keep != state->rcpt_left)
+ msg_panic("smtp_rcpt_cleanup: recipient count mismatch: %d+%d!=%d",
+ state->rcpt_drop, state->rcpt_keep, state->rcpt_left);
+
+ /*
+ * Recipients marked KEEP sort before recipients marked DROP. Skip the
+ * sorting in the common case that all recipients are marked the same.
+ */
+ if (state->rcpt_drop > 0 && state->rcpt_keep > 0)
+ qsort((void *) rcpt_list->info, state->rcpt_left,
+ sizeof(rcpt_list->info[0]), smtp_rcpt_cleanup_callback);
+
+ /*
+ * Truncate the recipient list and unmark the left-over recipients.
+ */
+ state->rcpt_left = state->rcpt_keep;
+ for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + state->rcpt_left; rcpt++)
+ rcpt->u.status = 0;
+ state->rcpt_drop = state->rcpt_keep = 0;
+}