1/*++
2/* NAME
3/*	bounce_notify_verp 3
4/* SUMMARY
5/*	send non-delivery report to sender, server side
6/* SYNOPSIS
7/*	#include "bounce_service.h"
8/*
9/*	int     bounce_notify_verp(flags, service, queue_name, queue_id, sender,
10/*					dsn_envid, dsn_ret, verp_delims,
11/*					templates)
12/*	int	flags;
13/*	char	*queue_name;
14/*	char	*queue_id;
15/*	char	*sender;
16/*	char	*dsn_envid;
17/*	int	dsn_ret;
18/*	char	*verp_delims;
19/*	BOUNCE_TEMPLATES *templates;
20/* DESCRIPTION
21/*	This module implements the server side of the bounce_notify()
22/*	(send bounce message) request. The logfile
23/*	is removed after and a warning is posted.
24/*	The bounce recipient address is encoded in VERP format.
25/*	This routine must be used for single bounces only.
26/*
27/*	When a message bounces, a full copy is sent to the originator,
28/*	and an optional  copy of the diagnostics with message headers is
29/*	sent to the postmaster.  The result is non-zero when the operation
30/*	should be tried again.
31/*
32/*	When a bounce is sent, the sender address is the empty
33/*	address.
34/* DIAGNOSTICS
35/*	Fatal error: error opening existing file.
36/* SEE ALSO
37/*	bounce(3) basic bounce service client interface
38/* LICENSE
39/* .ad
40/* .fi
41/*	The Secure Mailer license must be distributed with this software.
42/* AUTHOR(S)
43/*	Wietse Venema
44/*	IBM T.J. Watson Research
45/*	P.O. Box 704
46/*	Yorktown Heights, NY 10598, USA
47/*--*/
48
49/* System library. */
50
51#include <sys_defs.h>
52#include <fcntl.h>
53#include <errno.h>
54#include <string.h>
55#include <ctype.h>
56
57#ifdef STRCASECMP_IN_STRINGS_H
58#include <strings.h>
59#endif
60
61/* Utility library. */
62
63#include <msg.h>
64#include <vstream.h>
65#include <name_mask.h>
66
67/* Global library. */
68
69#include <mail_params.h>
70#include <mail_queue.h>
71#include <post_mail.h>
72#include <mail_addr.h>
73#include <mail_error.h>
74#include <verp_sender.h>
75#include <bounce.h>
76#include <dsn_mask.h>
77
78/* Application-specific. */
79
80#include "bounce_service.h"
81
82#define STR vstring_str
83
84/* bounce_notify_verp - send a bounce, VERP style */
85
86int     bounce_notify_verp(int flags, char *service, char *queue_name,
87			           char *queue_id, char *encoding,
88			           char *recipient, char *dsn_envid,
89			           int dsn_ret, char *verp_delims,
90			           BOUNCE_TEMPLATES *ts)
91{
92    const char *myname = "bounce_notify_verp";
93    BOUNCE_INFO *bounce_info;
94    int     bounce_status = 0;
95    int     postmaster_status;
96    VSTREAM *bounce;
97    int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
98				    var_notify_classes);
99    char   *postmaster;
100    VSTRING *verp_buf;
101    VSTRING *new_id;
102
103    /*
104     * Sanity checks. We must be called only for undeliverable non-bounce
105     * messages.
106     */
107    if (*recipient == 0)
108	msg_panic("%s: attempt to bounce a single bounce", myname);
109    if (strcasecmp(recipient, mail_addr_double_bounce()) == 0)
110	msg_panic("%s: attempt to bounce a double bounce", myname);
111
112    /*
113     * Initialize. Open queue file, bounce log, etc.
114     */
115    bounce_info = bounce_mail_init(service, queue_name, queue_id,
116				   encoding, dsn_envid, ts->failure);
117
118    /*
119     * If we have no recipient list then we can't send VERP replies. Send
120     * *something* anyway so that the mail is not lost in a black hole.
121     */
122    if (bounce_info->log_handle == 0) {
123	DSN_BUF *dsn_buf = dsb_create();
124	RCPT_BUF *rcpt_buf = rcpb_create();
125
126	dsb_simple(dsn_buf, "5.0.0", "(error report unavailable)");
127	(void) DSN_FROM_DSN_BUF(dsn_buf);
128	vstring_strcpy(rcpt_buf->address, "(recipient address unavailable)");
129	(void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
130	bounce_status = bounce_one_service(flags, queue_name, queue_id,
131					   encoding, recipient, dsn_envid,
132					   dsn_ret, rcpt_buf, dsn_buf, ts);
133	rcpb_free(rcpt_buf);
134	dsb_free(dsn_buf);
135	bounce_mail_free(bounce_info);
136	return (bounce_status);
137    }
138#define NULL_SENDER		MAIL_ADDR_EMPTY	/* special address */
139#define NULL_TRACE_FLAGS	0
140
141    /*
142     * A non-bounce message was returned. Send a single bounce, one per
143     * recipient.
144     */
145    verp_buf = vstring_alloc(100);
146    new_id = vstring_alloc(10);
147    while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
148			   bounce_info->dsn_buf) != 0) {
149	RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
150
151	/*
152	 * Notify the originator, subject to DSN NOTIFY restrictions.
153	 *
154	 * Fix 20090114: Use the Postfix original recipient, because that is
155	 * what the VERP consumer expects.
156	 */
157	if (rcpt->dsn_notify != 0		/* compat */
158	    && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
159	    bounce_status = 0;
160	} else {
161	    verp_sender(verp_buf, verp_delims, recipient, rcpt);
162	    if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
163						 INT_FILT_MASK_BOUNCE,
164						 NULL_TRACE_FLAGS,
165						 new_id)) != 0) {
166
167		/*
168		 * Send the bounce message header, some boilerplate text that
169		 * pretends that we are a polite mail system, the text with
170		 * reason for the bounce, and a copy of the original message.
171		 */
172		if (bounce_header(bounce, bounce_info, STR(verp_buf),
173				  NO_POSTMASTER_COPY) == 0
174		    && bounce_boilerplate(bounce, bounce_info) == 0
175		    && bounce_recipient_log(bounce, bounce_info) == 0
176		    && bounce_header_dsn(bounce, bounce_info) == 0
177		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
178		    bounce_original(bounce, bounce_info, dsn_ret ?
179				    dsn_ret : DSN_RET_FULL);
180		bounce_status = post_mail_fclose(bounce);
181		if (bounce_status == 0)
182		    msg_info("%s: sender non-delivery notification: %s",
183			     queue_id, STR(new_id));
184	    } else
185		bounce_status = 1;
186
187	    /*
188	     * Stop at the first sign of trouble, instead of making the
189	     * problem worse.
190	     */
191	    if (bounce_status != 0)
192		break;
193
194	    /*
195	     * Optionally, mark this recipient as done.
196	     */
197	    if (flags & BOUNCE_FLAG_DELRCPT)
198		bounce_delrcpt_one(bounce_info);
199	}
200
201	/*
202	 * Optionally, send a postmaster notice, subject to notify_classes
203	 * restrictions.
204	 *
205	 * This postmaster notice is not critical, so if it fails don't
206	 * retransmit the bounce that we just generated, just log a warning.
207	 */
208#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
209
210	if (SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE) {
211
212	    /*
213	     * Send the text with reason for the bounce, and the headers of
214	     * the original message. Don't bother sending the boiler-plate
215	     * text. This postmaster notice is not critical, so if it fails
216	     * don't retransmit the bounce that we just generated, just log a
217	     * warning.
218	     */
219	    postmaster = var_bounce_rcpt;
220	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
221						 postmaster,
222						 INT_FILT_MASK_BOUNCE,
223						 NULL_TRACE_FLAGS,
224						 new_id)) != 0) {
225		if (bounce_header(bounce, bounce_info, postmaster,
226				  POSTMASTER_COPY) == 0
227		    && bounce_recipient_log(bounce, bounce_info) == 0
228		    && bounce_header_dsn(bounce, bounce_info) == 0
229		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
230		    bounce_original(bounce, bounce_info, DSN_RET_HDRS);
231		postmaster_status = post_mail_fclose(bounce);
232		if (postmaster_status == 0)
233		    msg_info("%s: postmaster non-delivery notification: %s",
234			     queue_id, STR(new_id));
235	    } else
236		postmaster_status = 1;
237
238	    if (postmaster_status)
239		msg_warn("%s: postmaster notice failed while bouncing to %s",
240			 queue_id, recipient);
241	}
242    }
243
244    /*
245     * Examine the completion status. Delete the bounce log file only when
246     * the bounce was posted successfully, and only if we are bouncing for
247     * real, not just warning.
248     */
249    if (bounce_status == 0 && mail_queue_remove(service, queue_id)
250	&& errno != ENOENT)
251	msg_fatal("remove %s %s: %m", service, queue_id);
252
253    /*
254     * Cleanup.
255     */
256    bounce_mail_free(bounce_info);
257    vstring_free(verp_buf);
258    vstring_free(new_id);
259
260    return (bounce_status);
261}
262