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