1/*++
2/* NAME
3/*	bounce 3
4/* SUMMARY
5/*	bounce service client
6/* SYNOPSIS
7/*	#include <bounce.h>
8/*
9/*	int	bounce_append(flags, id, stats, recipient, relay, dsn)
10/*	int	flags;
11/*	const char *id;
12/*	MSG_STATS *stats;
13/*	RECIPIENT *rcpt;
14/*	const char *relay;
15/*	DSN	*dsn;
16/*
17/*	int	bounce_flush(flags, queue, id, encoding, sender,
18/*				dsn_envid, dsn_ret)
19/*	int	flags;
20/*	const char *queue;
21/*	const char *id;
22/*	const char *encoding;
23/*	const char *sender;
24/*	const char *dsn_envid;
25/*	int	dsn_ret;
26/*
27/*	int	bounce_flush_verp(flags, queue, id, encoding, sender,
28/*				dsn_envid, dsn_ret, verp_delims)
29/*	int	flags;
30/*	const char *queue;
31/*	const char *id;
32/*	const char *encoding;
33/*	const char *sender;
34/*	const char *dsn_envid;
35/*	int	dsn_ret;
36/*	const char *verp_delims;
37/*
38/*	int	bounce_one(flags, queue, id, encoding, sender, envid, ret,
39/*				stats, recipient, relay, dsn)
40/*	int	flags;
41/*	const char *queue;
42/*	const char *id;
43/*	const char *encoding;
44/*	const char *sender;
45/*	const char *dsn_envid;
46/*	int	dsn_ret;
47/*	MSG_STATS *stats;
48/*	RECIPIENT *rcpt;
49/*	const char *relay;
50/*	DSN	*dsn;
51/* DESCRIPTION
52/*	This module implements the client interface to the message
53/*	bounce service, which maintains a per-message log of status
54/*	records with recipients that were bounced, and the dsn_text why.
55/*
56/*	bounce_append() appends a dsn_text for non-delivery to the
57/*	bounce log for the named recipient, updates the address
58/*	verification service, or updates a message delivery record
59/*	on request by the sender. The flags argument determines
60/*	the action.
61/*
62/*	bounce_flush() actually bounces the specified message to
63/*	the specified sender, including the bounce log that was
64/*	built with bounce_append(). The bounce logfile is removed
65/*	upon successful completion.
66/*
67/*	bounce_flush_verp() is like bounce_flush(), but sends one
68/*	notification per recipient, with the failed recipient encoded
69/*	into the sender address.
70/*
71/*	bounce_one() bounces one recipient and immediately sends a
72/*	notification to the sender. This procedure does not append
73/*	the recipient and dsn_text to the per-message bounce log, and
74/*	should be used when a delivery agent changes the error
75/*	return address in a manner that depends on the recipient
76/*	address.
77/*
78/*	Arguments:
79/* .IP flags
80/*	The bitwise OR of zero or more of the following (specify
81/*	BOUNCE_FLAG_NONE to request no special processing):
82/* .RS
83/* .IP BOUNCE_FLAG_CLEAN
84/*	Delete the bounce log in case of an error (as in: pretend
85/*	that we never even tried to bounce this message).
86/* .IP BOUNCE_FLAG_DELRCPT
87/*	When specified with a flush request, request that
88/*	recipients be deleted from the queue file.
89/*
90/*	Note: the bounce daemon ignores this request when the
91/*	recipient queue file offset is <= 0.
92/* .IP DEL_REQ_FLAG_MTA_VRFY
93/*	The message is an MTA-requested address verification probe.
94/*	Update the address verification database instead of bouncing
95/*	mail.
96/* .IP DEL_REQ_FLAG_USR_VRFY
97/*	The message is a user-requested address expansion probe.
98/*	Update the message delivery record instead of bouncing mail.
99/* .IP DEL_REQ_FLAG_RECORD
100/*	This is a normal message with logged delivery. Update the
101/*	message delivery record and bounce the mail.
102/* .RE
103/* .IP queue
104/*	The message queue name of the original message file.
105/* .IP id
106/*	The message queue id if the original message file. The bounce log
107/*	file has the same name as the original message file.
108/* .IP stats
109/*	Time stamps from different message delivery stages
110/*	and session reuse count.
111/* .IP rcpt
112/*	Recipient information. See recipient_list(3).
113/* .IP relay
114/*	Name of the host that the message could not be delivered to.
115/*	This information is used for syslogging only.
116/* .IP encoding
117/*	The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
118/* .IP sender
119/*	The sender envelope address.
120/* .IP dsn_envid
121/*	Optional DSN envelope ID.
122/* .IP dsn_ret
123/*	Optional DSN return full/headers option.
124/* .IP dsn
125/*	Delivery status. See dsn(3). The specified action is ignored.
126/* .IP verp_delims
127/*	VERP delimiter characters, used when encoding the failed
128/*	sender into the envelope sender address.
129/* DIAGNOSTICS
130/*	In case of success, these functions log the action, and return a
131/*	zero value. Otherwise, the functions return a non-zero result,
132/*	and when BOUNCE_FLAG_CLEAN is disabled, log that message
133/*	delivery is deferred.
134/* BUGS
135/*	Should be replaced by routines with an attribute-value based
136/*	interface instead of an interface that uses a rigid argument list.
137/* LICENSE
138/* .ad
139/* .fi
140/*	The Secure Mailer license must be distributed with this software.
141/* AUTHOR(S)
142/*	Wietse Venema
143/*	IBM T.J. Watson Research
144/*	P.O. Box 704
145/*	Yorktown Heights, NY 10598, USA
146/*--*/
147
148/* System library. */
149
150#include <sys_defs.h>
151#include <string.h>
152
153/* Utility library. */
154
155#include <msg.h>
156#include <vstring.h>
157#include <mymalloc.h>
158
159/* Global library. */
160
161#include <mail_params.h>
162#include <mail_proto.h>
163#include <log_adhoc.h>
164#include <dsn_util.h>
165#include <rcpt_print.h>
166#include <dsn_print.h>
167#include <verify.h>
168#include <defer.h>
169#include <trace.h>
170#include <bounce.h>
171
172/* bounce_append - append dsn_text to per-message bounce log */
173
174int     bounce_append(int flags, const char *id, MSG_STATS *stats,
175		              RECIPIENT *rcpt, const char *relay,
176		              DSN *dsn)
177{
178    DSN     my_dsn = *dsn;
179    int     status;
180
181    /*
182     * Sanity check. If we're really confident, change this into msg_panic
183     * (remember, this information may be under control by a hostile server).
184     */
185    if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
186	msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status);
187	my_dsn.status = "5.0.0";
188    }
189
190    /*
191     * MTA-requested address verification information is stored in the verify
192     * service database.
193     */
194    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
195	my_dsn.action = "undeliverable";
196	status = verify_append(id, stats, rcpt, relay, &my_dsn,
197			       DEL_RCPT_STAT_BOUNCE);
198	return (status);
199    }
200
201    /*
202     * User-requested address verification information is logged and mailed
203     * to the requesting user.
204     */
205    if (flags & DEL_REQ_FLAG_USR_VRFY) {
206	my_dsn.action = "undeliverable";
207	status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
208	return (status);
209    }
210
211    /*
212     * Normal (well almost) delivery. When we're pretending that we can't
213     * bounce, don't create a defer log file when we wouldn't keep the bounce
214     * log file. That's a lot of negatives in one sentence.
215     */
216    else if (var_soft_bounce && (flags & BOUNCE_FLAG_CLEAN)) {
217	return (-1);
218    }
219
220    /*
221     * Normal mail delivery. May also send a delivery record to the user.
222     *
223     * XXX DSN We write all recipients to the bounce logfile regardless of DSN
224     * NOTIFY options, because those options don't apply to postmaster
225     * notifications.
226     */
227    else {
228	char   *my_status = mystrdup(my_dsn.status);
229	const char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
230
231	/*
232	 * Supply default action.
233	 */
234	my_dsn.status = my_status;
235	if (var_soft_bounce) {
236	    my_status[0] = '4';
237	    my_dsn.action = "delayed";
238	} else {
239	    my_dsn.action = "failed";
240	}
241
242	if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
243				var_defer_service : var_bounce_service,
244			   ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
245				ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
246				ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
247				ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
248				ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
249				ATTR_TYPE_END) == 0
250	    && ((flags & DEL_REQ_FLAG_RECORD) == 0
251		|| trace_append(flags, id, stats, rcpt, relay,
252				&my_dsn) == 0)) {
253	    log_adhoc(id, stats, rcpt, relay, &my_dsn, log_status);
254	    status = (var_soft_bounce ? -1 : 0);
255	} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
256	    VSTRING *junk = vstring_alloc(100);
257
258	    my_dsn.status = "4.3.0";
259	    vstring_sprintf(junk, "%s or %s service failure",
260			    var_bounce_service, var_trace_service);
261	    my_dsn.reason = vstring_str(junk);
262	    status = defer_append(flags, id, stats, rcpt, relay, &my_dsn);
263	    vstring_free(junk);
264	} else {
265	    status = -1;
266	}
267	myfree(my_status);
268	return (status);
269    }
270}
271
272/* bounce_flush - flush the bounce log and deliver to the sender */
273
274int     bounce_flush(int flags, const char *queue, const char *id,
275		             const char *encoding, const char *sender,
276		             const char *dsn_envid, int dsn_ret)
277{
278
279    /*
280     * When we're pretending that we can't bounce, don't send a bounce
281     * message.
282     */
283    if (var_soft_bounce)
284	return (-1);
285    if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
286			    ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_FLUSH,
287			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
288			    ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
289			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
290			    ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
291			    ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
292			    ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
293			    ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
294			    ATTR_TYPE_END) == 0) {
295	return (0);
296    } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
297	msg_info("%s: status=deferred (bounce failed)", id);
298	return (-1);
299    } else {
300	return (-1);
301    }
302}
303
304/* bounce_flush_verp - verpified notification */
305
306int     bounce_flush_verp(int flags, const char *queue, const char *id,
307			          const char *encoding, const char *sender,
308			          const char *dsn_envid, int dsn_ret,
309			          const char *verp_delims)
310{
311
312    /*
313     * When we're pretending that we can't bounce, don't send a bounce
314     * message.
315     */
316    if (var_soft_bounce)
317	return (-1);
318    if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
319			    ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_VERP,
320			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
321			    ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
322			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
323			    ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
324			    ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
325			    ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
326			    ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
327			    ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
328			    ATTR_TYPE_END) == 0) {
329	return (0);
330    } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
331	msg_info("%s: status=deferred (bounce failed)", id);
332	return (-1);
333    } else {
334	return (-1);
335    }
336}
337
338/* bounce_one - send notice for one recipient */
339
340int     bounce_one(int flags, const char *queue, const char *id,
341		           const char *encoding, const char *sender,
342		           const char *dsn_envid, int dsn_ret,
343		           MSG_STATS *stats, RECIPIENT *rcpt,
344		           const char *relay, DSN *dsn)
345{
346    DSN     my_dsn = *dsn;
347    int     status;
348
349    /*
350     * Sanity check.
351     */
352    if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
353	msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status);
354	my_dsn.status = "5.0.0";
355    }
356
357    /*
358     * MTA-requested address verification information is stored in the verify
359     * service database.
360     */
361    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
362	my_dsn.action = "undeliverable";
363	status = verify_append(id, stats, rcpt, relay, &my_dsn,
364			       DEL_RCPT_STAT_BOUNCE);
365	return (status);
366    }
367
368    /*
369     * User-requested address verification information is logged and mailed
370     * to the requesting user.
371     */
372    if (flags & DEL_REQ_FLAG_USR_VRFY) {
373	my_dsn.action = "undeliverable";
374	status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
375	return (status);
376    }
377
378    /*
379     * When we're not bouncing, then use the standard multi-recipient logfile
380     * based procedure.
381     */
382    else if (var_soft_bounce) {
383	return (bounce_append(flags, id, stats, rcpt, relay, &my_dsn));
384    }
385
386    /*
387     * Normal mail delivery. May also send a delivery record to the user.
388     *
389     * XXX DSN We send all recipients regardless of DSN NOTIFY options, because
390     * those options don't apply to postmaster notifications.
391     */
392    else {
393
394	/*
395	 * Supply default action.
396	 */
397	my_dsn.action = "failed";
398
399	if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
400			      ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_ONE,
401				ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
402				ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
403				ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
404				ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
405				ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
406			      ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
407				ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
408				ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
409				ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
410				ATTR_TYPE_END) == 0
411	    && ((flags & DEL_REQ_FLAG_RECORD) == 0
412		|| trace_append(flags, id, stats, rcpt, relay,
413				&my_dsn) == 0)) {
414	    log_adhoc(id, stats, rcpt, relay, &my_dsn, "bounced");
415	    status = 0;
416	} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
417	    VSTRING *junk = vstring_alloc(100);
418
419	    my_dsn.status = "4.3.0";
420	    vstring_sprintf(junk, "%s or %s service failure",
421			    var_bounce_service, var_trace_service);
422	    my_dsn.reason = vstring_str(junk);
423	    status = defer_append(flags, id, stats, rcpt, relay, &my_dsn);
424	    vstring_free(junk);
425	} else {
426	    status = -1;
427	}
428	return (status);
429    }
430}
431