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