summaryrefslogtreecommitdiffstats
path: root/src/smtp/smtp_rcpt.c
blob: 6608ea8804a7af6859729493b3a694fa8fae91df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*++
/* NAME
/*	smtp_rcpt 3
/* SUMMARY
/*	application-specific recipient list operations
/* SYNOPSIS
/*	#include <smtp.h>
/*
/*	SMTP_RCPT_INIT(state)
/*	SMTP_STATE *state;
/*
/*	SMTP_RCPT_DROP(state, rcpt)
/*	SMTP_STATE *state;
/*	RECIPIENT *rcpt;
/*
/*	SMTP_RCPT_KEEP(state, rcpt)
/*	SMTP_STATE *state;
/*	RECIPIENT *rcpt;
/*
/*	SMTP_RCPT_ISMARKED(rcpt)
/*	RECIPIENT *rcpt;
/*
/*	void	smtp_rcpt_cleanup(SMTP_STATE *state)
/*	SMTP_STATE *state;
/*
/*	int	SMTP_RCPT_LEFT(state)
/*	SMTP_STATE *state;
/*
/*	int	SMTP_RCPT_MARK_COUNT(state)
/*	SMTP_STATE *state;
/*
/*	void	smtp_rcpt_done(state, resp, rcpt)
/*	SMTP_STATE *state;
/*	SMTP_RESP *resp;
/*	RECIPIENT *rcpt;
/* DESCRIPTION
/*	This module implements application-specific mark and sweep
/*	operations on recipient lists. Operation is as follows:
/* .IP \(bu
/*	In the course of a delivery attempt each recipient is
/*	marked either as DROP (remove from recipient list) or KEEP
/*	(deliver to alternate mail server).
/* .IP \(bu
/*	After a delivery attempt any recipients marked DROP are deleted
/*	from the request, and the left-over recipients are unmarked.
/* .PP
/*	The mark/sweep algorithm is implemented in a redundant manner,
/*	and ensures that all recipients are explicitly accounted for.
/*
/*	Operations with upper case names are implemented by macros
/*	whose arguments may be evaluated more than once.
/*
/*	SMTP_RCPT_INIT() initializes application-specific recipient
/*	information and must be called before the first delivery attempt.
/*
/*	SMTP_RCPT_DROP() marks the specified recipient as DROP (remove
/*	from recipient list). It is an error to mark an already marked
/*	recipient.
/*
/*	SMTP_RCPT_KEEP() marks the specified recipient as KEEP (deliver
/*	to alternate mail server). It is an error to mark an already
/*	marked recipient.
/*
/*	SMTP_RCPT_ISMARKED() returns non-zero when the specified
/*	recipient is marked.
/*
/*	SMTP_RCPT_LEFT() returns the number of left_over recipients
/*	(the total number of marked and non-marked recipients).
/*
/*	SMTP_RCPT_MARK_COUNT() returns the number of left_over
/*	recipients that are marked.
/*
/*	smtp_rcpt_cleanup() cleans up the in-memory recipient list.
/*	It removes the recipients marked DROP from the left-over
/*	recipients, unmarks the left-over recipients, and enforces
/*	the requirement that all recipients are marked upon entry.
/*
/*	smtp_rcpt_done() logs that a recipient is completed and upon
/*	success it marks the recipient as done in the queue file.
/*	Finally, it marks the in-memory recipient as DROP.
/*
/*	Note: smtp_rcpt_done() may change the order of the recipient
/*	list.
/* DIAGNOSTICS
/*	Panic: interface violation.
/*
/*	When a recipient can't be logged as completed, the recipient is
/*	logged as deferred instead.
/* BUGS
/*	The single recipient list abstraction dates from the time
/*	that the SMTP client would give up after one SMTP session,
/*	so that each recipient was either bounced, delivered or
/*	deferred. Implicitly, all recipients were marked as DROP.
/*
/*	This abstraction is less convenient when an SMTP client
/*	must be able to deliver left-over recipients to a backup
/*	host. It might be more natural to have an input list with
/*	recipients to deliver, and an output list with left-over
/*	recipients.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System  library. */

#include <sys_defs.h>
#include <stdlib.h>			/* smtp_rcpt_cleanup  */
#include <string.h>

/* Utility  library. */

#include <msg.h>
#include <stringops.h>
#include <mymalloc.h>

/* Global library. */

#include <mail_params.h>
#include <deliver_request.h>		/* smtp_rcpt_done */
#include <deliver_completed.h>		/* smtp_rcpt_done */
#include <sent.h>			/* smtp_rcpt_done */
#include <dsn_mask.h>			/* smtp_rcpt_done */

/* Application-specific. */

#include <smtp.h>

/* smtp_rcpt_done - mark recipient as done or else */

void    smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
{
    DELIVER_REQUEST *request = state->request;
    SMTP_SESSION *session = state->session;
    SMTP_ITERATOR *iter = state->iterator;
    DSN_BUF *why = state->why;
    const char *dsn_action = "relayed";
    int     status;

    /*
     * Assume this was intermediate delivery when the server announced DSN
     * support, and don't send a DSN "SUCCESS" notification.
     */
    if (session->features & SMTP_FEATURE_DSN)
	rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;

    /*
     * Assume this was final delivery when the LMTP server announced no DSN
     * support. In backwards compatibility mode, send a "relayed" instead of
     * a "delivered" DSN "SUCCESS" notification. Do not attempt to "simplify"
     * the expression. The redundancy is for clarity. It is trivially
     * eliminated by the compiler. There is no need to sacrifice clarity for
     * the sake of "performance".
     */
    if ((session->features & SMTP_FEATURE_DSN) == 0
	&& !smtp_mode
	&& (smtp_cli_attr.flags & SMTP_CLI_FLAG_FINAL_DELIVERY) != 0)
	dsn_action = "delivered";

    /*
     * Report success and delete the recipient from the delivery request.
     * Defer if the success can't be reported.
     * 
     * Note: the DSN action is ignored in case of address probes.
     */
    dsb_update(why, resp->dsn, dsn_action, DSB_MTYPE_DNS, STR(iter->host),
	       DSB_DTYPE_SMTP, resp->str, "%s", resp->str);

    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
		  request->queue_id, &request->msg_stats, rcpt,
		  session->namaddrport, DSN_FROM_DSN_BUF(why));
    if (status == 0)
	if (request->flags & DEL_REQ_FLAG_SUCCESS)
	    deliver_completed(state->src, rcpt->offset);
    SMTP_RCPT_DROP(state, rcpt);
    state->status |= status;
}

/* smtp_rcpt_cleanup_callback - qsort callback */

static int smtp_rcpt_cleanup_callback(const void *a, const void *b)
{
    return (((RECIPIENT *) a)->u.status - ((RECIPIENT *) b)->u.status);
}

/* smtp_rcpt_cleanup - purge completed recipients from request */

void    smtp_rcpt_cleanup(SMTP_STATE *state)
{
    RECIPIENT_LIST *rcpt_list = &state->request->rcpt_list;
    RECIPIENT *rcpt;

    /*
     * Sanity checks.
     */
    if (state->rcpt_drop + state->rcpt_keep != state->rcpt_left)
	msg_panic("smtp_rcpt_cleanup: recipient count mismatch: %d+%d!=%d",
		  state->rcpt_drop, state->rcpt_keep, state->rcpt_left);

    /*
     * Recipients marked KEEP sort before recipients marked DROP. Skip the
     * sorting in the common case that all recipients are marked the same.
     */
    if (state->rcpt_drop > 0 && state->rcpt_keep > 0)
	qsort((void *) rcpt_list->info, state->rcpt_left,
	      sizeof(rcpt_list->info[0]), smtp_rcpt_cleanup_callback);

    /*
     * Truncate the recipient list and unmark the left-over recipients.
     */
    state->rcpt_left = state->rcpt_keep;
    for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + state->rcpt_left; rcpt++)
	rcpt->u.status = 0;
    state->rcpt_drop = state->rcpt_keep = 0;
}