xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_rcpt.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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