summaryrefslogtreecommitdiffstats
path: root/src/bounce/bounce_trace_service.c
blob: 8a62305fcd12e8905b7316d27b200dd6551a85e9 (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
/*++
/* NAME
/*	bounce_trace_service 3
/* SUMMARY
/*	send status report to sender, server side
/* SYNOPSIS
/*	#include "bounce_service.h"
/*
/*	int     bounce_trace_service(flags, service, queue_name, queue_id,
/*					encoding, smtputf8, sender, envid,
/*					ret, templates)
/*	int	flags;
/*	char	*service;
/*	char	*queue_name;
/*	char	*queue_id;
/*	char	*encoding;
/*	int	smtputf8;
/*	char	*sender;
/*	char	*envid;
/*	int	ret;
/*	BOUNCE_TEMPLATES *templates;
/* DESCRIPTION
/*	This module implements the server side of the trace_flush()
/*	(send delivery notice) request. The logfile
/*	is removed after the notice is posted.
/*
/*	A status report includes a prelude with human-readable text,
/*	a DSN-style report, and the email message that was subject of
/*	the status report.
/*
/*	When a status report is sent, the sender address is the empty
/*	address.
/* DIAGNOSTICS
/*	Fatal error: error opening existing file.
/* BUGS
/* SEE ALSO
/*	bounce(3) basic bounce service client interface
/* 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
/*--*/

/* System library. */

#include <sys_defs.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>

/* Utility library. */

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

/* Global library. */

#include <mail_params.h>
#include <mail_queue.h>
#include <post_mail.h>
#include <mail_addr.h>
#include <mail_error.h>
#include <dsn_mask.h>
#include <rec_type.h>
#include <deliver_request.h>		/* USR_VRFY and RECORD flags */

/* Application-specific. */

#include "bounce_service.h"

#define STR vstring_str

/* bounce_trace_service - send a delivery status notice */

int     bounce_trace_service(int flags, char *service, char *queue_name,
			             char *queue_id, char *encoding,
			             int smtputf8,
			             char *recipient, char *dsn_envid,
			             int unused_dsn_ret,
			             BOUNCE_TEMPLATES *ts)
{
    BOUNCE_INFO *bounce_info;
    int     bounce_status = 1;
    VSTREAM *bounce;
    int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
				    var_notify_classes);
    VSTRING *new_id;
    int     count;
    const char *sender;

    /*
     * For consistency with fail/delay notifications, send notification for a
     * non-bounce message as a single-bounce message, send notification for a
     * single-bounce message as a double-bounce message, and drop requests to
     * send notification for a double-bounce message.
     */
#define NULL_SENDER		MAIL_ADDR_EMPTY	/* special address */

    if (strcasecmp_utf8(recipient, mail_addr_double_bounce()) == 0) {
	msg_info("%s: not sending trace/success notification for "
		 "double-bounce message", queue_id);
	return (0);
    } else if (*recipient == 0) {
	if ((notify_mask & MAIL_ERROR_2BOUNCE) != 0) {
	    recipient = var_2bounce_rcpt;
	    sender = mail_addr_double_bounce();
	} else {
	    msg_info("%s: not sending trace/success notification "
		     "for single-bounce message", queue_id);
	    if (mail_queue_remove(service, queue_id) && errno != ENOENT)
		msg_fatal("remove %s %s: %m", service, queue_id);
	    return (0);
	}
    } else {
	/* Always send notification for non-bounce message. */
	sender = NULL_SENDER;
    }

    /*
     * Initialize. Open queue file, bounce log, etc.
     * 
     * XXX DSN The trace service produces information from the trace logfile
     * which is used for three types of reports:
     * 
     * a) "what-if" reports that show what would happen without actually
     * delivering mail (sendmail -bv).
     * 
     * b) A report of actual deliveries (sendmail -v).
     * 
     * c) DSN NOTIFY=SUCCESS reports of successful delivery ("delivered",
     * "expanded" or "relayed").
     */
#define NON_DSN_FLAGS (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD)

    bounce_info = bounce_mail_init(service, queue_name, queue_id,
				   encoding, smtputf8, dsn_envid,
				   flags & NON_DSN_FLAGS ?
				   ts->verify : ts->success);

    /*
     * XXX With multi-recipient mail some queue file recipients may have
     * NOTIFY=SUCCESS and others not. Depending on what subset of recipients
     * are delivered, a trace file may or may not be created. Even when the
     * last partial delivery attempt had no NOTIFY=SUCCESS recipients, a
     * trace file may still exist from a previous partial delivery attempt.
     * So as long as any recipient in the original queue file had
     * NOTIFY=SUCCESS we have to always look for the trace file and be
     * prepared for the file not to exist.
     * 
     * See also comments in qmgr/qmgr_active.c.
     */
    if (bounce_info->log_handle == 0) {
	if (msg_verbose)
	    msg_info("%s: no trace file -- not sending a notification",
		     queue_id);
	bounce_mail_free(bounce_info);
	return (0);
    }
#define NULL_TRACE_FLAGS	0

    /*
     * Send a single bounce with a template message header, some boilerplate
     * text that pretends that we are a polite mail system, the text with
     * per-recipient status, and a copy of the original message.
     * 
     * XXX DSN We use the same trace file for "what-if", "verbose delivery" and
     * "success" delivery reports. This saves file system overhead because
     * there are fewer potential left-over files to remove up when we create
     * a new queue file.
     */
    new_id = vstring_alloc(10);
    if ((bounce = post_mail_fopen_nowait(sender, recipient,
					 MAIL_SRC_MASK_BOUNCE,
					 NULL_TRACE_FLAGS,
					 smtputf8,
					 new_id)) != 0) {
	count = -1;
	if (bounce_header(bounce, bounce_info, recipient,
			  NO_POSTMASTER_COPY) == 0
	    && bounce_boilerplate(bounce, bounce_info) == 0
	    && (count = bounce_diagnostic_log(bounce, bounce_info,
					      DSN_NOTIFY_OVERRIDE)) > 0
	    && bounce_header_dsn(bounce, bounce_info) == 0
	    && bounce_diagnostic_dsn(bounce, bounce_info,
				     DSN_NOTIFY_OVERRIDE) > 0) {
	    bounce_original(bounce, bounce_info, DSN_RET_HDRS);
	    bounce_status = post_mail_fclose(bounce);
	    if (bounce_status == 0)
		msg_info("%s: sender delivery status notification: %s",
			 queue_id, STR(new_id));
	} else {
	    (void) vstream_fclose(bounce);
	    if (count == 0)
		bounce_status = 0;
	}
    }

    /*
     * Examine the completion status. Delete the trace log file only when the
     * status notice was posted successfully.
     */
    if (bounce_status == 0 && mail_queue_remove(service, queue_id)
	&& errno != ENOENT)
	msg_fatal("remove %s %s: %m", service, queue_id);

    /*
     * Cleanup.
     */
    bounce_mail_free(bounce_info);
    vstring_free(new_id);

    return (bounce_status);
}