1/*++ 2/* NAME 3/* smtp_rcpt 3 4/* SUMMARY 5/* application-specific recipient list operations 6/* SYNOPSIS 7/* #include <smtp.h> 8/* 9/* SMTP_RCPT_INIT(state) 10/* SMTP_STATE *state; 11/* 12/* SMTP_RCPT_DROP(state, rcpt) 13/* SMTP_STATE *state; 14/* RECIPIENT *rcpt; 15/* 16/* SMTP_RCPT_KEEP(state, rcpt) 17/* SMTP_STATE *state; 18/* RECIPIENT *rcpt; 19/* 20/* SMTP_RCPT_ISMARKED(rcpt) 21/* RECIPIENT *rcpt; 22/* 23/* void smtp_rcpt_cleanup(SMTP_STATE *state) 24/* SMTP_STATE *state; 25/* 26/* int SMTP_RCPT_LEFT(state) 27/* SMTP_STATE *state; 28/* 29/* void smtp_rcpt_done(state, resp, rcpt) 30/* SMTP_STATE *state; 31/* SMTP_RESP *resp; 32/* RECIPIENT *rcpt; 33/* DESCRIPTION 34/* This module implements application-specific mark and sweep 35/* operations on recipient lists. Operation is as follows: 36/* .IP \(bu 37/* In the course of a delivery attempt each recipient is 38/* marked either as DROP (remove from recipient list) or KEEP 39/* (deliver to alternate mail server). 40/* .IP \(bu 41/* After a delivery attempt any recipients marked DROP are deleted 42/* from the request, and the left-over recipients are unmarked. 43/* .PP 44/* The mark/sweep algorithm is implemented in a redundant manner, 45/* and ensures that all recipients are explicitly accounted for. 46/* 47/* Operations with upper case names are implemented by macros 48/* whose arguments may be evaluated more than once. 49/* 50/* SMTP_RCPT_INIT() initializes application-specific recipient 51/* information and must be called before the first delivery attempt. 52/* 53/* SMTP_RCPT_DROP() marks the specified recipient as DROP (remove 54/* from recipient list). It is an error to mark an already marked 55/* recipient. 56/* 57/* SMTP_RCPT_KEEP() marks the specified recipient as KEEP (deliver 58/* to alternate mail server). It is an error to mark an already 59/* marked recipient. 60/* 61/* SMTP_RCPT_ISMARKED() returns non-zero when the specified 62/* recipient is marked. 63/* 64/* SMTP_RCPT_LEFT() returns the number of left_over recipients 65/* (the total number of marked and non-marked recipients). 66/* 67/* smtp_rcpt_cleanup() cleans up the in-memory recipient list. 68/* It removes the recipients marked DROP from the left-over 69/* recipients, unmarks the left-over recipients, and enforces 70/* the requirement that all recipients are marked upon entry. 71/* 72/* smtp_rcpt_done() logs that a recipient is completed and upon 73/* success it marks the recipient as done in the queue file. 74/* Finally, it marks the in-memory recipient as DROP. 75/* 76/* Note: smtp_rcpt_done() may change the order of the recipient 77/* list. 78/* DIAGNOSTICS 79/* Panic: interface violation. 80/* 81/* When a recipient can't be logged as completed, the recipient is 82/* logged as deferred instead. 83/* BUGS 84/* The single recipient list abstraction dates from the time 85/* that the SMTP client would give up after one SMTP session, 86/* so that each recipient was either bounced, delivered or 87/* deferred. Implicitly, all recipients were marked as DROP. 88/* 89/* This abstraction is less convenient when an SMTP client 90/* must be able to deliver left-over recipients to a backup 91/* host. It might be more natural to have an input list with 92/* recipients to deliver, and an output list with left-over 93/* recipients. 94/* LICENSE 95/* .ad 96/* .fi 97/* The Secure Mailer license must be distributed with this software. 98/* AUTHOR(S) 99/* Wietse Venema 100/* IBM T.J. Watson Research 101/* P.O. Box 704 102/* Yorktown Heights, NY 10598, USA 103/*--*/ 104 105/* System library. */ 106 107#include <sys_defs.h> 108#include <stdlib.h> /* smtp_rcpt_cleanup */ 109#include <string.h> 110 111/* Utility library. */ 112 113#include <msg.h> 114#include <stringops.h> 115#include <mymalloc.h> 116 117/* Global library. */ 118 119#include <mail_params.h> 120#include <deliver_request.h> /* smtp_rcpt_done */ 121#include <deliver_completed.h> /* smtp_rcpt_done */ 122#include <sent.h> /* smtp_rcpt_done */ 123#include <dsn_mask.h> /* smtp_rcpt_done */ 124 125/* Application-specific. */ 126 127#include <smtp.h> 128 129/* smtp_rcpt_done - mark recipient as done or else */ 130 131void smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt) 132{ 133 DELIVER_REQUEST *request = state->request; 134 SMTP_SESSION *session = state->session; 135 DSN_BUF *why = state->why; 136 const char *dsn_action = "relayed"; 137 int status; 138 139 /* 140 * Assume this was intermediate delivery when the server announced DSN 141 * support, and don't send a DSN "SUCCESS" notification. 142 */ 143 if (session->features & SMTP_FEATURE_DSN) 144 rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS; 145 146 /* 147 * Assume this was final delivery when the LMTP server announced no DSN 148 * support. In backwards compatibility mode, send a "relayed" instead of 149 * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify" 150 * the expression. The redundancy is for clarity. It is trivially 151 * eliminated by the compiler. There is no need to sacrifice clarity for 152 * the sake of "performance". 153 */ 154 if ((session->features & SMTP_FEATURE_DSN) == 0 155 && (state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) != 0 156 && var_lmtp_assume_final != 0) 157 dsn_action = "delivered"; 158 159 /* 160 * Report success and delete the recipient from the delivery request. 161 * Defer if the success can't be reported. 162 * 163 * Note: the DSN action is ignored in case of address probes. 164 */ 165 dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, session->host, 166 DSB_DTYPE_SMTP, resp->str, "%s", resp->str); 167 168 status = sent(DEL_REQ_TRACE_FLAGS(request->flags), 169 request->queue_id, &request->msg_stats, rcpt, 170 session->namaddrport, DSN_FROM_DSN_BUF(why)); 171 if (status == 0) 172 if (request->flags & DEL_REQ_FLAG_SUCCESS) 173 deliver_completed(state->src, rcpt->offset); 174 SMTP_RCPT_DROP(state, rcpt); 175 state->status |= status; 176} 177 178/* smtp_rcpt_cleanup_callback - qsort callback */ 179 180static int smtp_rcpt_cleanup_callback(const void *a, const void *b) 181{ 182 return (((RECIPIENT *) a)->u.status - ((RECIPIENT *) b)->u.status); 183} 184 185/* smtp_rcpt_cleanup - purge completed recipients from request */ 186 187void smtp_rcpt_cleanup(SMTP_STATE *state) 188{ 189 RECIPIENT_LIST *rcpt_list = &state->request->rcpt_list; 190 RECIPIENT *rcpt; 191 192 /* 193 * Sanity checks. 194 */ 195 if (state->rcpt_drop + state->rcpt_keep != state->rcpt_left) 196 msg_panic("smtp_rcpt_cleanup: recipient count mismatch: %d+%d!=%d", 197 state->rcpt_drop, state->rcpt_keep, state->rcpt_left); 198 199 /* 200 * Recipients marked KEEP sort before recipients marked DROP. Skip the 201 * sorting in the common case that all recipients are marked the same. 202 */ 203 if (state->rcpt_drop > 0 && state->rcpt_keep > 0) 204 qsort((void *) rcpt_list->info, state->rcpt_left, 205 sizeof(rcpt_list->info[0]), smtp_rcpt_cleanup_callback); 206 207 /* 208 * Truncate the recipient list and unmark the left-over recipients. 209 */ 210 state->rcpt_left = state->rcpt_keep; 211 for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + state->rcpt_left; rcpt++) 212 rcpt->u.status = 0; 213 state->rcpt_drop = state->rcpt_keep = 0; 214} 215