~ubuntu-branches/ubuntu/trusty/postfix/trusty-updates

« back to all changes in this revision

Viewing changes to src/bounce/bounce_notify_util.c

Tags: upstream-2.3.1
ImportĀ upstreamĀ versionĀ 2.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
/*      } BOUNCE_INFO;
14
14
/*
15
15
/*      BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id,
16
 
/*                                      encoding, flush)
 
16
/*                                      encoding, dsn_envid, template)
17
17
/*      const char *service;
18
18
/*      const char *queue_name;
19
19
/*      const char *queue_id;
20
20
/*      const char *encoding;
21
 
/*      int     flush;
 
21
/*      const char *dsn_envid;
 
22
/*      const BOUNCE_TEMPLATE *template;
22
23
/*
23
 
/*      BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id,
24
 
/*                                      encoding, orig_recipient,
25
 
/*                                      recipient, dsn_status,
26
 
/*                                      dsn_action, why)
 
24
/*      BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding,
 
25
/*                                      dsn_envid, dsn_notify, rcpt_buf,
 
26
/*                                      dsn_buf, template)
27
27
/*      const char *queue_name;
28
28
/*      const char *queue_id;
29
29
/*      const char *encoding;
30
 
/*      const char *orig_recipient;
31
 
/*      const char *recipient;
32
 
/*      const char *status;
33
 
/*      const char *why;
 
30
/*      int     dsn_notify;
 
31
/*      const char *dsn_envid;
 
32
/*      RCPT_BUF *rcpt_buf;
 
33
/*      DSN_BUF *dsn_buf;
 
34
/*      const BOUNCE_TEMPLATE *template;
34
35
/*
35
36
/*      void    bounce_mail_free(bounce_info)
36
37
/*      BOUNCE_INFO *bounce_info;
37
38
/*
38
 
/*      int     bounce_header(fp, bounce_info, recipient)
 
39
/*      int     bounce_header(fp, bounce_info, recipient, postmaster_copy)
39
40
/*      VSTREAM *fp;
40
41
/*      BOUNCE_INFO *bounce_info;
41
42
/*      const char *recipient;
 
43
/*      int     postmaster_copy;
42
44
/*
43
45
/*      int     bounce_boilerplate(fp, bounce_info)
44
46
/*      VSTREAM *fp;
48
50
/*      VSTREAM *fp;
49
51
/*      BOUNCE_INFO *bounce_info;
50
52
/*
51
 
/*      int     bounce_diagnostic_log(fp, bounce_info)
 
53
/*      int     bounce_diagnostic_log(fp, bounce_info, notify_filter)
52
54
/*      VSTREAM *fp;
53
55
/*      BOUNCE_INFO *bounce_info;
 
56
/*      int     notify_filter;
54
57
/*
55
58
/*      int     bounce_header_dsn(fp, bounce_info)
56
59
/*      VSTREAM *fp;
60
63
/*      VSTREAM *fp;
61
64
/*      BOUNCE_INFO *bounce_info;
62
65
/*
63
 
/*      int     bounce_diagnostic_dsn(fp, bounce_info)
 
66
/*      int     bounce_diagnostic_dsn(fp, bounce_info, notify_filter)
64
67
/*      VSTREAM *fp;
65
68
/*      BOUNCE_INFO *bounce_info;
 
69
/*      int     notify_filter;
66
70
/*
67
71
/*      int     bounce_original(fp, bounce_info, headers_only)
68
72
/*      VSTREAM *fp;
92
96
/*
93
97
/*      bounce_header() produces a standard mail header with the specified
94
98
/*      recipient and starts a text/plain message segment for the
95
 
/*      human-readable problem description.
 
99
/*      human-readable problem description. postmaster_copy is either
 
100
/*      POSTMASTER_COPY or NO_POSTMASTER_COPY.
96
101
/*
97
102
/*      bounce_boilerplate() produces the standard "sorry" text that
98
103
/*      creates the illusion that mail systems are civilized.
102
107
/*      and with the text why the recipient was undeliverable.
103
108
/*
104
109
/*      bounce_diagnostic_log() sends a human-readable representation of
105
 
/*      logfile information for all undeliverable recipients. This routine
106
 
/*      will become obsolete when individual recipients of the same message
107
 
/*      can have different sender addresses to bounce to.
 
110
/*      logfile information for all undeliverable recipients. The
 
111
/*      notify_filter specifies what recipient status records should be
 
112
/*      reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
 
113
/*      In the absence of DSN NOTIFY information all records are reported.
 
114
/*      The result value is -1 in case of error, the number of reported
 
115
/*      recipients in case of success.
108
116
/*
109
117
/*      bounce_header_dsn() starts a message/delivery-status message
110
118
/*      segment and sends the machine-readable information that identifies
115
123
/*      and with the text why the recipient was undeliverable.
116
124
/*
117
125
/*      bounce_diagnostic_dsn() sends a machine-readable representation of
118
 
/*      logfile information for all undeliverable recipients. This routine
119
 
/*      will become obsolete when individual recipients of the same message
120
 
/*      can have different sender addresses to bounce to.
 
126
/*      logfile information for all undeliverable recipients. The
 
127
/*      notify_filter specifies what recipient status records should be
 
128
/*      reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
 
129
/*      In the absence of DSN NOTIFY information all records are reported.
 
130
/*      The result value is -1 in case of error, the number of reported
 
131
/*      recipients in case of success.
121
132
/*
122
 
/*      bounce_original() starts a message/rfc822 or headers/rfc822
123
 
/*      message segment and sends the original message, either full or
124
 
/*      message headers only.
 
133
/*      bounce_original() starts a message/rfc822 or text/rfc822-headers
 
134
/*      message segment and sends the original message, either full
 
135
/*      (DSN_RET_FULL) or message headers only (DSN_RET_HDRS).
125
136
/*
126
137
/*      bounce_delrcpt() deletes recipients in the logfile from the original
127
138
/*      queue file.
129
140
/*      bounce_delrcpt_one() deletes one recipient from the original
130
141
/*      queue file.
131
142
/* DIAGNOSTICS
132
 
/*      Fatal error: error opening existing file. Warnings: corrupt
133
 
/*      message file. A corrupt message is saved to the "corrupt"
134
 
/*      queue for further inspection.
 
143
/*      Fatal error: error opening existing file.
135
144
/* BUGS
136
145
/* SEE ALSO
137
146
/*      bounce(3) basic bounce service client interface
149
158
/* System library. */
150
159
 
151
160
#include <sys_defs.h>
 
161
#include <sys/stat.h>
152
162
#include <stdlib.h>
 
163
#include <stdio.h>                      /* sscanf() */
153
164
#include <unistd.h>
154
165
#include <errno.h>
155
166
#include <string.h>
168
179
#include <vstream.h>
169
180
#include <line_wrap.h>
170
181
#include <stringops.h>
171
 
#include <xtext.h>
172
182
#include <myflock.h>
173
183
 
174
184
/* Global library. */
187
197
#include <mail_proto.h>
188
198
#include <lex_822.h>
189
199
#include <deliver_completed.h>
 
200
#include <dsn_mask.h>
190
201
 
191
202
/* Application-specific. */
192
203
 
200
211
                                              const char *queue_name,
201
212
                                              const char *queue_id,
202
213
                                              const char *encoding,
203
 
                                              int flush,
 
214
                                              const char *dsn_envid,
 
215
                                              RCPT_BUF *rcpt_buf,
 
216
                                              DSN_BUF *dsn_buf,
 
217
                                              BOUNCE_TEMPLATE *template,
204
218
                                              BOUNCE_LOG *log_handle)
205
219
{
206
220
    BOUNCE_INFO *bounce_info;
207
221
    int     rec_type;
208
222
 
209
223
    /*
210
 
     * Bundle up a bunch of parameters and initialize information. that will
 
224
     * Bundle up a bunch of parameters and initialize information that will
211
225
     * be discovered on the fly.
212
226
     */
213
227
    bounce_info = (BOUNCE_INFO *) mymalloc(sizeof(*bounce_info));
224
238
                     bounce_info->queue_id, encoding);
225
239
        bounce_info->mime_encoding = 0;
226
240
    }
227
 
    bounce_info->flush = flush;
 
241
    if (dsn_envid && *dsn_envid)
 
242
        bounce_info->dsn_envid = dsn_envid;
 
243
    else
 
244
        bounce_info->dsn_envid = 0;
 
245
    bounce_info->template = template;
228
246
    bounce_info->buf = vstring_alloc(100);
229
247
    bounce_info->sender = vstring_alloc(100);
230
248
    bounce_info->arrival_time = 0;
231
249
    bounce_info->orig_offs = 0;
 
250
    bounce_info->message_size = 0;
 
251
    bounce_info->rcpt_buf = rcpt_buf;
 
252
    bounce_info->dsn_buf = dsn_buf;
232
253
    bounce_info->log_handle = log_handle;
233
254
 
234
255
    /*
262
283
        msg_fatal("open %s %s: %m", service, queue_id);
263
284
 
264
285
    /*
265
 
     * Skip over the original message envelope records. If the envelope is
266
 
     * corrupted just send whatever we can (remember this is a best effort,
267
 
     * it does not have to be perfect).
 
286
     * Get time/size/sender information from the original message envelope
 
287
     * records. If the envelope is corrupted just send whatever we can
 
288
     * (remember this is a best effort, it does not have to be perfect).
268
289
     * 
269
290
     * Lock the file for shared use, so that queue manager leaves it alone after
270
291
     * restarting.
278
299
                      VSTREAM_PATH(bounce_info->orig_fp));
279
300
        while ((rec_type = rec_get(bounce_info->orig_fp,
280
301
                                   bounce_info->buf, 0)) > 0) {
281
 
            if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
282
 
                if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
 
302
 
 
303
            /*
 
304
             * Postfix version dependent: data offset in SIZE record.
 
305
             */
 
306
            if (rec_type == REC_TYPE_SIZE) {
 
307
                if (bounce_info->message_size == 0)
 
308
                    sscanf(STR(bounce_info->buf), "%ld %ld",
 
309
                           &bounce_info->message_size,
 
310
                           &bounce_info->orig_offs);
 
311
                if (bounce_info->message_size < 0)
 
312
                    bounce_info->message_size = 0;
 
313
                if (bounce_info->orig_offs < 0)
 
314
                    bounce_info->orig_offs = 0;
 
315
            }
 
316
 
 
317
            /*
 
318
             * Information for the Arrival-Date: attribute.
 
319
             */
 
320
            else if (rec_type == REC_TYPE_TIME) {
 
321
                if (bounce_info->arrival_time == 0
 
322
                    && (bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
283
323
                    bounce_info->arrival_time = 0;
284
 
            } else if (rec_type == REC_TYPE_FROM) {
 
324
            }
 
325
 
 
326
            /*
 
327
             * Information for the X-Postfix-Sender: attribute.
 
328
             */
 
329
            else if (rec_type == REC_TYPE_FROM) {
285
330
                quote_822_local_flags(bounce_info->sender,
286
331
                                      VSTRING_LEN(bounce_info->buf) ?
287
332
                                      STR(bounce_info->buf) :
288
333
                                      mail_addr_mail_daemon(), 0);
289
 
            } else if (rec_type == REC_TYPE_MESG) {
 
334
            }
 
335
 
 
336
            /*
 
337
             * Backwards compatibility: no data offset in SIZE record.
 
338
             */
 
339
            else if (rec_type == REC_TYPE_MESG) {
290
340
                /* XXX Future: sender+recipient after message content. */
291
341
                if (VSTRING_LEN(bounce_info->sender) == 0)
292
342
                    msg_warn("%s: no sender before message content record",
294
344
                bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
295
345
                break;
296
346
            }
 
347
            if (bounce_info->orig_offs > 0
 
348
                && bounce_info->arrival_time > 0
 
349
                && VSTRING_LEN(bounce_info->sender) > 0)
 
350
                break;
297
351
        }
298
352
    }
299
353
    return (bounce_info);
305
359
                                      const char *queue_name,
306
360
                                      const char *queue_id,
307
361
                                      const char *encoding,
308
 
                                      int flush)
 
362
                                      const char *dsn_envid,
 
363
                                      BOUNCE_TEMPLATE *template)
309
364
{
310
365
    BOUNCE_INFO *bounce_info;
311
366
    BOUNCE_LOG *log_handle;
 
367
    RCPT_BUF *rcpt_buf;
 
368
    DSN_BUF *dsn_buf;
312
369
 
313
370
    /*
314
371
     * Initialize the bounce_info structure. If the bounce log cannot be
318
375
     * job. But if the system IS running out of resources, raise a fatal
319
376
     * run-time error and force a backoff.
320
377
     */
321
 
    if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0
322
 
        && errno != ENOENT)
323
 
        msg_fatal("open %s %s: %m", service, queue_id);
324
 
    bounce_info = bounce_mail_alloc(service, queue_name, queue_id,
325
 
                                    encoding, flush, log_handle);
 
378
    if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0) {
 
379
        if (errno != ENOENT)
 
380
            msg_fatal("open %s %s: %m", service, queue_id);
 
381
        rcpt_buf = 0;
 
382
        dsn_buf = 0;
 
383
    } else {
 
384
        rcpt_buf = rcpb_create();
 
385
        dsn_buf = dsb_create();
 
386
    }
 
387
    bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding,
 
388
                                    dsn_envid, rcpt_buf, dsn_buf,
 
389
                                    template, log_handle);
326
390
    return (bounce_info);
327
391
}
328
392
 
331
395
BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
332
396
                                          const char *queue_id,
333
397
                                          const char *encoding,
334
 
                                          const char *orig_recipient,
335
 
                                          const char *recipient,
336
 
                                          long offset,
337
 
                                          const char *dsn_status,
338
 
                                          const char *dsn_action,
339
 
                                          const char *why)
 
398
                                          const char *dsn_envid,
 
399
                                          RCPT_BUF *rcpt_buf,
 
400
                                          DSN_BUF *dsn_buf,
 
401
                                          BOUNCE_TEMPLATE *template)
340
402
{
341
403
    BOUNCE_INFO *bounce_info;
342
 
    BOUNCE_LOG *log_handle;
343
404
 
344
405
    /*
345
 
     * Initialize the bounce_info structure. Forge a logfile record for just
346
 
     * one recipient.
 
406
     * Initialize the bounce_info structure for just one recipient.
347
407
     */
348
 
    log_handle = bounce_log_forge(orig_recipient, recipient, offset, dsn_status,
349
 
                                  dsn_action, why);
350
 
    bounce_info = bounce_mail_alloc("none", queue_name, queue_id,
351
 
                                    encoding, BOUNCE_MSG_FAIL, log_handle);
 
408
    bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding,
 
409
                                    dsn_envid, rcpt_buf, dsn_buf, template,
 
410
                                    (BOUNCE_LOG *) 0);
352
411
    return (bounce_info);
353
412
}
354
413
 
356
415
 
357
416
void    bounce_mail_free(BOUNCE_INFO *bounce_info)
358
417
{
359
 
    if (bounce_info->log_handle && bounce_log_close(bounce_info->log_handle))
360
 
        msg_warn("%s: read bounce log %s: %m",
361
 
                 bounce_info->queue_id, bounce_info->queue_id);
 
418
    if (bounce_info->log_handle) {
 
419
        if (bounce_log_close(bounce_info->log_handle))
 
420
            msg_warn("%s: read bounce log %s: %m",
 
421
                     bounce_info->queue_id, bounce_info->queue_id);
 
422
        rcpb_free(bounce_info->rcpt_buf);
 
423
        dsb_free(bounce_info->dsn_buf);
 
424
    }
362
425
    if (bounce_info->orig_fp && vstream_fclose(bounce_info->orig_fp))
363
426
        msg_warn("%s: read message file %s %s: %m",
364
427
                 bounce_info->queue_id, bounce_info->queue_name,
373
436
/* bounce_header - generate bounce message header */
374
437
 
375
438
int     bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
376
 
                              const char *dest)
 
439
                              const char *dest, int postmaster_copy)
377
440
{
 
441
    BOUNCE_TEMPLATE *template = bounce_info->template;
378
442
 
379
443
    /*
380
444
     * Print a minimal bounce header. The cleanup service will add other
382
446
     */
383
447
#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
384
448
 
385
 
    post_mail_fprintf(bounce, "From: %s (Mail Delivery System)",
386
 
                      MAIL_ADDR_MAIL_DAEMON);
387
 
 
388
 
    /*
389
 
     * Non-delivery subject line.
390
 
     */
391
 
    if (bounce_info->flush == BOUNCE_MSG_FAIL) {
392
 
        post_mail_fputs(bounce, dest == var_bounce_rcpt
393
 
                     || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
394
 
                        "Subject: Postmaster Copy: Undelivered Mail" :
395
 
                        "Subject: Undelivered Mail Returned to Sender");
396
 
    }
397
 
 
398
 
    /*
399
 
     * Delayed mail subject line.
400
 
     */
401
 
    else if (bounce_info->flush == BOUNCE_MSG_WARN) {
402
 
        post_mail_fputs(bounce, dest == var_bounce_rcpt
403
 
                     || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
404
 
                        "Subject: Postmaster Warning: Delayed Mail" :
405
 
                        "Subject: Delayed Mail (still being retried)");
406
 
    }
407
 
 
408
 
    /*
409
 
     * Address verification or delivery report.
410
 
     */
411
 
    else {
412
 
        post_mail_fputs(bounce, "Subject: Mail Delivery Status Report");
413
 
    }
414
 
 
415
 
    post_mail_fprintf(bounce, "To: %s",
416
 
                      STR(quote_822_local(bounce_info->buf, dest)));
417
 
 
418
 
    /*
419
 
     * MIME header.
 
449
    /*
 
450
     * Generic headers.
 
451
     */
 
452
    bounce_template_headers(post_mail_fprintf, bounce, template,
 
453
                            STR(quote_822_local(bounce_info->buf, dest)),
 
454
                            postmaster_copy);
 
455
 
 
456
    /*
 
457
     * Auto-Submitted header, as per RFC 3834.
 
458
     */
 
459
    post_mail_fprintf(bounce, "Auto-Submitted: %s", postmaster_copy ?
 
460
                      "auto-generated" : "auto-replied");
 
461
 
 
462
    /*
 
463
     * MIME header. Use 8bit encoding when either the bounced message or the
 
464
     * template requires it.
420
465
     */
421
466
    post_mail_fprintf(bounce, "MIME-Version: 1.0");
422
467
    post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
424
469
    post_mail_fprintf(bounce, "\tboundary=\"%s\"", bounce_info->mime_boundary);
425
470
    if (bounce_info->mime_encoding)
426
471
        post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
 
472
                     STREQ(bounce_info->mime_encoding, MAIL_ATTR_ENC_7BIT) ?
 
473
                          bounce_template_encoding(template) :
427
474
                          bounce_info->mime_encoding);
428
475
    post_mail_fputs(bounce, "");
429
476
    post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
434
481
    post_mail_fputs(bounce, "");
435
482
    post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
436
483
    post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
437
 
    post_mail_fprintf(bounce, "Content-Type: %s", "text/plain");
 
484
    post_mail_fprintf(bounce, "Content-Type: %s; charset=%s",
 
485
                      "text/plain", bounce_template_charset(template));
438
486
    post_mail_fputs(bounce, "");
439
487
 
440
488
    return (vstream_ferror(bounce));
446
494
{
447
495
 
448
496
    /*
449
 
     * Print the message body with the problem report. XXX For now, we use a
450
 
     * fixed bounce template. We could use a site-specific parametrized
451
 
     * template with ${name} macros and we could do wonderful things such as
452
 
     * word wrapping to make the text look nicer. No matter how hard we would
453
 
     * try, receiving bounced mail will always suck.
 
497
     * Print the boiler-plate text.
454
498
     */
455
 
#define UNDELIVERED(flush) \
456
 
        ((flush) == BOUNCE_MSG_FAIL || (flush) == BOUNCE_MSG_WARN)
457
 
 
458
 
    post_mail_fprintf(bounce, "This is the %s program at host %s.",
459
 
                      var_mail_name, var_myhostname);
460
 
    post_mail_fputs(bounce, "");
461
 
    if (bounce_info->flush == BOUNCE_MSG_FAIL) {
462
 
        post_mail_fputs(bounce,
463
 
               "I'm sorry to have to inform you that your message could not");
464
 
        post_mail_fputs(bounce,
465
 
               "be delivered to one or more recipients. It's attached below.");
466
 
    } else if (bounce_info->flush == BOUNCE_MSG_WARN) {
467
 
        post_mail_fputs(bounce,
468
 
                        "####################################################################");
469
 
        post_mail_fputs(bounce,
470
 
                        "# THIS IS A WARNING ONLY.  YOU DO NOT NEED TO RESEND YOUR MESSAGE. #");
471
 
        post_mail_fputs(bounce,
472
 
                        "####################################################################");
473
 
        post_mail_fputs(bounce, "");
474
 
        post_mail_fprintf(bounce,
475
 
                      "Your message could not be delivered for %.1f hours.",
476
 
                          var_delay_warn_time / 3600.0);
477
 
        post_mail_fprintf(bounce,
478
 
                          "It will be retried until it is %.1f days old.",
479
 
                          var_max_queue_time / 86400.0);
480
 
    } else {
481
 
        post_mail_fputs(bounce,
482
 
                "Enclosed is the mail delivery report that you requested.");
483
 
    }
484
 
    if (UNDELIVERED(bounce_info->flush)) {
485
 
        post_mail_fputs(bounce, "");
486
 
        post_mail_fprintf(bounce,
487
 
                          "For further assistance, please send mail to <%s>",
488
 
                          MAIL_ADDR_POSTMASTER);
489
 
        post_mail_fputs(bounce, "");
490
 
        post_mail_fprintf(bounce,
491
 
               "If you do so, please include this problem report. You can");
492
 
        post_mail_fprintf(bounce,
493
 
                   "delete your own text from the attached returned message.");
494
 
    }
495
 
    post_mail_fputs(bounce, "");
496
 
    post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
 
499
    bounce_template_expand(post_mail_fputs, bounce, bounce_info->template);
497
500
    return (vstream_ferror(bounce));
498
501
}
499
502
 
527
530
 
528
531
int     bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
529
532
{
 
533
    RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
 
534
    DSN    *dsn = &bounce_info->dsn_buf->dsn;
530
535
 
531
536
    /*
532
537
     * Mask control and non-ASCII characters (done in bounce_log_read()),
534
539
     * piped into other programs. Sort of like TCP Wrapper's safe_finger
535
540
     * program.
536
541
     */
 
542
#define NON_NULL_EMPTY(s) ((s) && *(s))
 
543
 
537
544
    post_mail_fputs(bounce, "");
538
 
    if (bounce_info->log_handle->orig_rcpt) {
 
545
    if (NON_NULL_EMPTY(rcpt->orig_addr)) {
539
546
        bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s",
540
 
                          bounce_info->log_handle->recipient,
541
 
                          bounce_info->log_handle->orig_rcpt,
542
 
                          bounce_info->log_handle->text);
 
547
                          rcpt->address, rcpt->orig_addr, dsn->reason);
543
548
    } else {
544
549
        bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
545
 
                          bounce_info->log_handle->recipient,
546
 
                          bounce_info->log_handle->text);
 
550
                          rcpt->address, dsn->reason);
547
551
    }
548
552
    return (vstream_ferror(bounce));
549
553
}
550
554
 
551
555
/* bounce_diagnostic_log - send bounce log report */
552
556
 
553
 
int     bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
 
557
int     bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
 
558
                                      int notify_filter)
554
559
{
 
560
    RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
 
561
    int     count = 0;
555
562
 
556
563
    /*
557
 
     * Append a copy of the delivery error log. We're doing a best effort, so
558
 
     * there is no point raising a fatal run-time error in case of a logfile
559
 
     * read error.
 
564
     * Append a human-readable copy of the delivery error log. We're doing a
 
565
     * best effort, so there is no point raising a fatal run-time error in
 
566
     * case of a logfile read error.
 
567
     * 
 
568
     * XXX DSN If the logfile with failed recipients is unavailable, pretend
 
569
     * that we found something anyway, so that this notification will not be
 
570
     * canceled.
560
571
     */
561
572
    if (bounce_info->log_handle == 0
562
573
        || bounce_log_rewind(bounce_info->log_handle)) {
563
 
        post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
 
574
        if (IS_FAILURE_TEMPLATE(bounce_info->template)) {
 
575
            post_mail_fputs(bounce, "");
 
576
            post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
 
577
            count = 1;                          /* XXX don't abort */
 
578
        }
564
579
    } else {
565
 
        while (bounce_log_read(bounce_info->log_handle) != 0)
566
 
            if (bounce_recipient_log(bounce, bounce_info) != 0)
567
 
                break;
 
580
        while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
 
581
                               bounce_info->dsn_buf) != 0) {
 
582
            if (rcpt->dsn_notify == 0           /* compat */
 
583
                || (rcpt->dsn_notify & notify_filter)) {
 
584
                count++;
 
585
                if (bounce_recipient_log(bounce, bounce_info) != 0)
 
586
                    break;
 
587
            }
 
588
        }
568
589
    }
569
 
    return (vstream_ferror(bounce));
 
590
    return (vstream_ferror(bounce) ? -1 : count);
570
591
}
571
592
 
572
593
/* bounce_header_dsn - send per-MTA bounce DSN records */
594
615
#if 0
595
616
    post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
596
617
#endif
 
618
    if (NON_NULL_EMPTY(bounce_info->dsn_envid)) {
 
619
        post_mail_fprintf(bounce, "Original-Envelope-Id: %s",
 
620
                          bounce_info->dsn_envid);
 
621
    }
597
622
    post_mail_fprintf(bounce, "X-%s-Queue-ID: %s",
598
623
                      bounce_info->mail_name, bounce_info->queue_id);
599
624
    if (VSTRING_LEN(bounce_info->sender) > 0)
609
634
 
610
635
int     bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
611
636
{
 
637
    RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
 
638
    DSN    *dsn = &bounce_info->dsn_buf->dsn;
 
639
 
612
640
    post_mail_fputs(bounce, "");
613
 
    post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
614
 
                      bounce_info->log_handle->recipient);
615
 
    if (bounce_info->log_handle->orig_rcpt) {
 
641
    post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", rcpt->address);
 
642
 
 
643
    /*
 
644
     * XXX DSN
 
645
     * 
 
646
     * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this
 
647
     * recipient, the Original-Recipient field MUST NOT appear."
 
648
     * 
 
649
     * This is inconsistent with section 5.2.1.d: "If no ORCPT parameter was
 
650
     * present in the RCPT command when the message was received, an ORCPT
 
651
     * parameter MAY be added to the RCPT command when the message is
 
652
     * relayed.". Postfix adds an ORCPT parameter under these conditions.
 
653
     * 
 
654
     * Therefore, all down-stream MTAs will send DSNs with Original-Recipient
 
655
     * field ontaining this same ORCPT value. When a down-stream MTA can use
 
656
     * that information in their DSNs, it makes no sense that an up-stream
 
657
     * MTA can't use that same information in its own DSNs.
 
658
     * 
 
659
     * Postfix always reports an Original-Recipient field, because it is more
 
660
     * more useful and more consistent.
 
661
     */
 
662
    if (NON_NULL_EMPTY(rcpt->dsn_orcpt)) {
 
663
        post_mail_fprintf(bounce, "Original-Recipient: %s", rcpt->dsn_orcpt);
 
664
    } else if (NON_NULL_EMPTY(rcpt->orig_addr)) {
616
665
        post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s",
617
 
                          bounce_info->log_handle->orig_rcpt);
 
666
                          rcpt->orig_addr);
618
667
    }
619
668
    post_mail_fprintf(bounce, "Action: %s",
620
 
                      bounce_info->flush == BOUNCE_MSG_FAIL ?
621
 
                      "failed" : bounce_info->log_handle->dsn_action);
622
 
    post_mail_fprintf(bounce, "Status: %s",
623
 
                      bounce_info->log_handle->dsn_status);
624
 
    bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
625
 
                      bounce_info->mail_name, bounce_info->log_handle->text);
 
669
                      IS_FAILURE_TEMPLATE(bounce_info->template) ?
 
670
                      "failed" : dsn->action);
 
671
    post_mail_fprintf(bounce, "Status: %s", dsn->status);
 
672
    if (NON_NULL_EMPTY(dsn->mtype) && NON_NULL_EMPTY(dsn->mname))
 
673
        bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s",
 
674
                          dsn->mtype, dsn->mname);
 
675
    if (NON_NULL_EMPTY(dsn->dtype) && NON_NULL_EMPTY(dsn->dtext))
 
676
        bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s",
 
677
                          dsn->dtype, dsn->dtext);
 
678
    else
 
679
        bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
 
680
                          bounce_info->mail_name, dsn->reason);
626
681
#if 0
627
 
    post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
628
 
                      bounce_info->log_handle->log_time);
 
682
    if (dsn->time > 0)
 
683
        post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
 
684
                          mail_date(dsn->time));
629
685
#endif
630
 
    if (bounce_info->flush == BOUNCE_MSG_WARN)
 
686
    if (IS_DELAY_TEMPLATE(bounce_info->template))
631
687
        post_mail_fprintf(bounce, "Will-Retry-Until: %s",
632
688
                 mail_date(bounce_info->arrival_time + var_max_queue_time));
633
689
    return (vstream_ferror(bounce));
635
691
 
636
692
/* bounce_diagnostic_dsn - send bounce log report, machine readable form */
637
693
 
638
 
int     bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
 
694
int     bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
 
695
                                      int notify_filter)
639
696
{
 
697
    RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
 
698
    int     count = 0;
640
699
 
641
700
    /*
642
 
     * Append a copy of the delivery error log. We're doing a best effort, so
643
 
     * there is no point raising a fatal run-time error in case of a logfile
644
 
     * read error.
 
701
     * Append a machine-readable copy of the delivery error log. We're doing
 
702
     * a best effort, so there is no point raising a fatal run-time error in
 
703
     * case of a logfile read error.
 
704
     * 
 
705
     * XXX DSN If the logfile with failed recipients is unavailable, pretend
 
706
     * that we found something anyway, so that this notification will not be
 
707
     * canceled.
645
708
     */
646
 
    if (bounce_info->log_handle != 0
647
 
        && bounce_log_rewind(bounce_info->log_handle) == 0) {
648
 
        while (bounce_log_read(bounce_info->log_handle) != 0)
649
 
            if (bounce_recipient_dsn(bounce, bounce_info) != 0)
650
 
                break;
 
709
    if (bounce_info->log_handle == 0
 
710
        || bounce_log_rewind(bounce_info->log_handle)) {
 
711
        if (IS_FAILURE_TEMPLATE(bounce_info->template))
 
712
            count = 1;                          /* XXX don't abort */
 
713
    } else {
 
714
        while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
 
715
                               bounce_info->dsn_buf) != 0) {
 
716
            if (rcpt->dsn_notify == 0           /* compat */
 
717
                || (rcpt->dsn_notify & notify_filter)) {
 
718
                count++;
 
719
                if (bounce_recipient_dsn(bounce, bounce_info) != 0)
 
720
                    break;
 
721
            }
 
722
        }
651
723
    }
652
 
    return (vstream_ferror(bounce));
 
724
    return (vstream_ferror(bounce) ? -1 : count);
653
725
}
654
726
 
655
727
/* bounce_original - send a copy of the original to the victim */
659
731
{
660
732
    int     status = 0;
661
733
    int     rec_type = 0;
662
 
    int     bounce_length;
 
734
 
 
735
    /*
 
736
     * When truncating a large message, don't damage the MIME structure: send
 
737
     * the message headers only.
 
738
     */
 
739
    if (var_bounce_limit > 0
 
740
        && bounce_info->orig_fp
 
741
        && (bounce_info->message_size <= 0
 
742
            || bounce_info->message_size > var_bounce_limit))
 
743
        headers_only = DSN_RET_HDRS;
663
744
 
664
745
    /*
665
746
     * MIME headers.
666
747
     */
 
748
#define IS_UNDELIVERED_TEMPLATE(template) \
 
749
        (IS_FAILURE_TEMPLATE(template) || IS_DELAY_TEMPLATE(template))
 
750
 
667
751
    post_mail_fputs(bounce, "");
668
752
    post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
669
753
    post_mail_fprintf(bounce, "Content-Description: %s%s",
670
 
                      UNDELIVERED(bounce_info->flush) ? "Undelivered " : "",
671
 
                      headers_only ? "Message Headers" : "Message");
672
 
    post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
 
754
                      IS_UNDELIVERED_TEMPLATE(bounce_info->template) ?
 
755
                      "Undelivered " : "",
 
756
                      headers_only == DSN_RET_HDRS ?
 
757
                      "Message Headers" : "Message");
 
758
    post_mail_fprintf(bounce, "Content-Type: %s",
 
759
                      headers_only == DSN_RET_HDRS ?
673
760
                      "text/rfc822-headers" : "message/rfc822");
674
761
    if (bounce_info->mime_encoding)
675
762
        post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
686
773
    }
687
774
 
688
775
    /*
689
 
     * Copy the original message contents. Limit the amount of bounced text
690
 
     * so there is a better chance of the bounce making it back. We're doing
691
 
     * raw record output here so that we don't throw away binary transparency
692
 
     * yet.
 
776
     * Copy the original message contents. We're doing raw record output here
 
777
     * so that we don't throw away binary transparency yet.
693
778
     */
694
779
#define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s))
695
780
 
696
 
    bounce_length = 0;
697
781
    while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) {
698
782
        if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
699
783
            break;
700
 
        if (headers_only && !IS_HEADER(vstring_str(bounce_info->buf)))
701
 
            break;
702
 
        if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
703
 
            bounce_length += VSTRING_LEN(bounce_info->buf) + 2;
704
 
            status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
705
 
        } else
706
 
            break;
 
784
        if (headers_only == DSN_RET_HDRS
 
785
            && !IS_HEADER(vstring_str(bounce_info->buf)))
 
786
            break;
 
787
        status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
707
788
    }
708
789
 
709
790
    /*
723
804
 
724
805
void    bounce_delrcpt(BOUNCE_INFO *bounce_info)
725
806
{
 
807
    RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
 
808
 
726
809
    if (bounce_info->orig_fp != 0
727
810
        && bounce_info->log_handle != 0
728
811
        && bounce_log_rewind(bounce_info->log_handle) == 0)
729
 
        while (bounce_log_read(bounce_info->log_handle) != 0)
730
 
            if (bounce_info->log_handle->rcpt_offset > 0)
731
 
                deliver_completed(bounce_info->orig_fp,
732
 
                                  bounce_info->log_handle->rcpt_offset);
 
812
        while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
 
813
                               bounce_info->dsn_buf) != 0)
 
814
            if (rcpt->offset > 0)
 
815
                deliver_completed(bounce_info->orig_fp, rcpt->offset);
733
816
}
734
817
 
735
818
/* bounce_delrcpt_one - delete one recipient from original queue file */
736
819
 
737
820
void    bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
738
821
{
739
 
    if (bounce_info->orig_fp != 0
740
 
        && bounce_info->log_handle != 0
741
 
        && bounce_info->log_handle->rcpt_offset > 0)
742
 
        deliver_completed(bounce_info->orig_fp,
743
 
                          bounce_info->log_handle->rcpt_offset);
 
822
    RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
 
823
 
 
824
    if (bounce_info->orig_fp != 0 && rcpt->offset > 0)
 
825
        deliver_completed(bounce_info->orig_fp, rcpt->offset);
744
826
}