smtp_rcpt.c revision 1.2
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
139void    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
189static 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
196void    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