7 /* application-specific recipient list operations
11 /* SMTP_RCPT_INIT(state)
14 /* SMTP_RCPT_DROP(state, rcpt)
18 /* SMTP_RCPT_KEEP(state, rcpt)
22 /* SMTP_RCPT_ISMARKED(rcpt)
25 /* void smtp_rcpt_cleanup(SMTP_STATE *state)
28 /* int SMTP_RCPT_LEFT(state)
31 /* void smtp_rcpt_done(state, resp, rcpt)
36 /* This module implements application-specific mark and sweep
37 /* operations on recipient lists. Operation is as follows:
39 /* In the course of a delivery attempt each recipient is
40 /* marked either as DROP (remove from recipient list) or KEEP
41 /* (deliver to alternate mail server).
43 /* After a delivery attempt any recipients marked DROP are deleted
44 /* from the request, and the left-over recipients are unmarked.
46 /* The mark/sweep algorithm is implemented in a redundant manner,
47 /* and ensures that all recipients are explicitly accounted for.
49 /* Operations with upper case names are implemented by macros
50 /* whose arguments may be evaluated more than once.
52 /* SMTP_RCPT_INIT() initializes application-specific recipient
53 /* information and must be called before the first delivery attempt.
55 /* SMTP_RCPT_DROP() marks the specified recipient as DROP (remove
56 /* from recipient list). It is an error to mark an already marked
59 /* SMTP_RCPT_KEEP() marks the specified recipient as KEEP (deliver
60 /* to alternate mail server). It is an error to mark an already
63 /* SMTP_RCPT_ISMARKED() returns non-zero when the specified
64 /* recipient is marked.
66 /* SMTP_RCPT_LEFT() returns the number of left_over recipients
67 /* (the total number of marked and non-marked recipients).
69 /* smtp_rcpt_cleanup() cleans up the in-memory recipient list.
70 /* It removes the recipients marked DROP from the left-over
71 /* recipients, unmarks the left-over recipients, and enforces
72 /* the requirement that all recipients are marked upon entry.
74 /* smtp_rcpt_done() logs that a recipient is completed and upon
75 /* success it marks the recipient as done in the queue file.
76 /* Finally, it marks the in-memory recipient as DROP.
78 /* Note: smtp_rcpt_done() may change the order of the recipient
81 /* Panic: interface violation.
83 /* When a recipient can't be logged as completed, the recipient is
84 /* logged as deferred instead.
86 /* The single recipient list abstraction dates from the time
87 /* that the SMTP client would give up after one SMTP session,
88 /* so that each recipient was either bounced, delivered or
89 /* deferred. Implicitly, all recipients were marked as DROP.
91 /* This abstraction is less convenient when an SMTP client
92 /* must be able to deliver left-over recipients to a backup
93 /* host. It might be more natural to have an input list with
94 /* recipients to deliver, and an output list with left-over
99 /* The Secure Mailer license must be distributed with this software.
102 /* IBM T.J. Watson Research
104 /* Yorktown Heights, NY 10598, USA
107 /* System library. */
109 #include <sys_defs.h>
110 #include <stdlib.h> /* smtp_rcpt_cleanup */
113 /* Utility library. */
116 #include <stringops.h>
117 #include <mymalloc.h>
119 /* Global library. */
121 #include <mail_params.h>
122 #include <deliver_request.h> /* smtp_rcpt_done */
123 #include <deliver_completed.h> /* smtp_rcpt_done */
124 #include <sent.h> /* smtp_rcpt_done */
125 #include <dsn_mask.h> /* smtp_rcpt_done */
127 /* Application-specific. */
131 /* smtp_rcpt_done - mark recipient as done or else */
133 void smtp_rcpt_done(SMTP_STATE
*state
, SMTP_RESP
*resp
, RECIPIENT
*rcpt
)
135 DELIVER_REQUEST
*request
= state
->request
;
136 SMTP_SESSION
*session
= state
->session
;
137 DSN_BUF
*why
= state
->why
;
138 const char *dsn_action
= "relayed";
142 * Assume this was intermediate delivery when the server announced DSN
143 * support, and don't send a DSN "SUCCESS" notification.
145 if (session
->features
& SMTP_FEATURE_DSN
)
146 rcpt
->dsn_notify
&= ~DSN_NOTIFY_SUCCESS
;
149 * Assume this was final delivery when the LMTP server announced no DSN
150 * support. In backwards compatibility mode, send a "relayed" instead of
151 * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify"
152 * the expression. The redundancy is for clarity. It is trivially
153 * eliminated by the compiler. There is no need to sacrifice clarity for
154 * the sake of "performance".
156 if ((session
->features
& SMTP_FEATURE_DSN
) == 0
157 && (state
->misc_flags
& SMTP_MISC_FLAG_USE_LMTP
) != 0
158 && var_lmtp_assume_final
!= 0)
159 dsn_action
= "delivered";
162 * Report success and delete the recipient from the delivery request.
163 * Defer if the success can't be reported.
165 * Note: the DSN action is ignored in case of address probes.
167 dsb_update(why
, resp
->dsn
, dsn_action
, DSB_MTYPE_DNS
, session
->host
,
168 DSB_DTYPE_SMTP
, resp
->str
, "%s", resp
->str
);
170 status
= sent(DEL_REQ_TRACE_FLAGS(request
->flags
),
171 request
->queue_id
, &request
->msg_stats
, rcpt
,
172 session
->namaddrport
, DSN_FROM_DSN_BUF(why
));
174 if (request
->flags
& DEL_REQ_FLAG_SUCCESS
)
175 deliver_completed(state
->src
, rcpt
->offset
);
176 SMTP_RCPT_DROP(state
, rcpt
);
177 state
->status
|= status
;
180 /* smtp_rcpt_cleanup_callback - qsort callback */
182 static int smtp_rcpt_cleanup_callback(const void *a
, const void *b
)
184 return (((RECIPIENT
*) a
)->u
.status
- ((RECIPIENT
*) b
)->u
.status
);
187 /* smtp_rcpt_cleanup - purge completed recipients from request */
189 void smtp_rcpt_cleanup(SMTP_STATE
*state
)
191 RECIPIENT_LIST
*rcpt_list
= &state
->request
->rcpt_list
;
197 if (state
->rcpt_drop
+ state
->rcpt_keep
!= state
->rcpt_left
)
198 msg_panic("smtp_rcpt_cleanup: recipient count mismatch: %d+%d!=%d",
199 state
->rcpt_drop
, state
->rcpt_keep
, state
->rcpt_left
);
202 * Recipients marked KEEP sort before recipients marked DROP. Skip the
203 * sorting in the common case that all recipients are marked the same.
205 if (state
->rcpt_drop
> 0 && state
->rcpt_keep
> 0)
206 qsort((void *) rcpt_list
->info
, state
->rcpt_left
,
207 sizeof(rcpt_list
->info
[0]), smtp_rcpt_cleanup_callback
);
210 * Truncate the recipient list and unmark the left-over recipients.
212 state
->rcpt_left
= state
->rcpt_keep
;
213 for (rcpt
= rcpt_list
->info
; rcpt
< rcpt_list
->info
+ state
->rcpt_left
; rcpt
++)
215 state
->rcpt_drop
= state
->rcpt_keep
= 0;