bounce_notify_util.c [plain text]
#include <sys_defs.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <mymalloc.h>
#include <events.h>
#include <vstring.h>
#include <vstream.h>
#include <line_wrap.h>
#include <stringops.h>
#include <xtext.h>
#include <myflock.h>
#include <mail_queue.h>
#include <quote_822_local.h>
#include <mail_params.h>
#include <is_header.h>
#include <record.h>
#include <rec_type.h>
#include <post_mail.h>
#include <mail_addr.h>
#include <mail_error.h>
#include <bounce_log.h>
#include <mail_date.h>
#include <mail_proto.h>
#include <lex_822.h>
#include <deliver_completed.h>
#include "bounce_service.h"
#define STR vstring_str
static BOUNCE_INFO *bounce_mail_alloc(const char *service,
const char *queue_name,
const char *queue_id,
const char *encoding,
int flush,
BOUNCE_LOG *log_handle)
{
BOUNCE_INFO *bounce_info;
int rec_type;
bounce_info = (BOUNCE_INFO *) mymalloc(sizeof(*bounce_info));
bounce_info->service = service;
bounce_info->queue_name = queue_name;
bounce_info->queue_id = queue_id;
if (strcmp(encoding, MAIL_ATTR_ENC_8BIT) == 0) {
bounce_info->mime_encoding = "8bit";
} else if (strcmp(encoding, MAIL_ATTR_ENC_7BIT) == 0) {
bounce_info->mime_encoding = "7bit";
} else {
if (strcmp(encoding, MAIL_ATTR_ENC_NONE) != 0)
msg_warn("%s: unknown encoding: %.200s",
bounce_info->queue_id, encoding);
bounce_info->mime_encoding = 0;
}
bounce_info->flush = flush;
bounce_info->buf = vstring_alloc(100);
bounce_info->sender = vstring_alloc(100);
bounce_info->arrival_time = 0;
bounce_info->orig_offs = 0;
bounce_info->log_handle = log_handle;
bounce_info->mail_name = mystrdup(var_mail_name);
translit(bounce_info->mail_name, " \t\r\n()<>@,;:\\\".[]",
"-----------------");
vstring_sprintf(bounce_info->buf, "%s.%lu/%s",
queue_id, (unsigned long) event_time(), var_myhostname);
bounce_info->mime_boundary = mystrdup(STR(bounce_info->buf));
if ((bounce_info->orig_fp = mail_queue_open(queue_name, queue_id,
O_RDWR, 0)) == 0
&& errno != ENOENT)
msg_fatal("open %s %s: %m", service, queue_id);
#define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
if (bounce_info->orig_fp != 0) {
if (myflock(vstream_fileno(bounce_info->orig_fp), INTERNAL_LOCK,
DELIVER_LOCK_MODE) < 0)
msg_fatal("cannot get shared lock on %s: %m",
VSTREAM_PATH(bounce_info->orig_fp));
while ((rec_type = rec_get(bounce_info->orig_fp,
bounce_info->buf, 0)) > 0) {
if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
bounce_info->arrival_time = 0;
} else if (rec_type == REC_TYPE_FROM) {
quote_822_local_flags(bounce_info->sender,
VSTRING_LEN(bounce_info->buf) ?
STR(bounce_info->buf) :
mail_addr_mail_daemon(), 0);
} else if (rec_type == REC_TYPE_MESG) {
if (VSTRING_LEN(bounce_info->sender) == 0)
msg_warn("%s: no sender before message content record",
bounce_info->queue_id);
bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
break;
}
}
}
return (bounce_info);
}
BOUNCE_INFO *bounce_mail_init(const char *service,
const char *queue_name,
const char *queue_id,
const char *encoding,
int flush)
{
BOUNCE_INFO *bounce_info;
BOUNCE_LOG *log_handle;
if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0
&& errno != ENOENT)
msg_fatal("open %s %s: %m", service, queue_id);
bounce_info = bounce_mail_alloc(service, queue_name, queue_id,
encoding, flush, log_handle);
return (bounce_info);
}
BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
const char *queue_id,
const char *encoding,
const char *orig_recipient,
const char *recipient,
long offset,
const char *dsn_status,
const char *dsn_action,
const char *why)
{
BOUNCE_INFO *bounce_info;
BOUNCE_LOG *log_handle;
log_handle = bounce_log_forge(orig_recipient, recipient, offset, dsn_status,
dsn_action, why);
bounce_info = bounce_mail_alloc("none", queue_name, queue_id,
encoding, BOUNCE_MSG_FAIL, log_handle);
return (bounce_info);
}
void bounce_mail_free(BOUNCE_INFO *bounce_info)
{
if (bounce_info->log_handle && bounce_log_close(bounce_info->log_handle))
msg_warn("%s: read bounce log %s: %m",
bounce_info->queue_id, bounce_info->queue_id);
if (bounce_info->orig_fp && vstream_fclose(bounce_info->orig_fp))
msg_warn("%s: read message file %s %s: %m",
bounce_info->queue_id, bounce_info->queue_name,
bounce_info->queue_id);
vstring_free(bounce_info->buf);
vstring_free(bounce_info->sender);
myfree(bounce_info->mail_name);
myfree((char *) bounce_info->mime_boundary);
myfree((char *) bounce_info);
}
int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
const char *dest)
{
#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
post_mail_fprintf(bounce, "From: %s (Mail Delivery System)",
MAIL_ADDR_MAIL_DAEMON);
if (bounce_info->flush == BOUNCE_MSG_FAIL) {
post_mail_fputs(bounce, dest == var_bounce_rcpt
|| dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
"Subject: Postmaster Copy: Undelivered Mail" :
"Subject: Undelivered Mail Returned to Sender");
}
else if (bounce_info->flush == BOUNCE_MSG_WARN) {
post_mail_fputs(bounce, dest == var_bounce_rcpt
|| dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
"Subject: Postmaster Warning: Delayed Mail" :
"Subject: Delayed Mail (still being retried)");
}
else {
post_mail_fputs(bounce, "Subject: Mail Delivery Status Report");
}
post_mail_fprintf(bounce, "To: %s",
STR(quote_822_local(bounce_info->buf, dest)));
post_mail_fprintf(bounce, "MIME-Version: 1.0");
post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
"multipart/report", "delivery-status");
post_mail_fprintf(bounce, "\tboundary=\"%s\"", bounce_info->mime_boundary);
if (bounce_info->mime_encoding)
post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
bounce_info->mime_encoding);
post_mail_fputs(bounce, "");
post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
post_mail_fprintf(bounce, "Content-Type: %s", "text/plain");
post_mail_fputs(bounce, "");
return (vstream_ferror(bounce));
}
int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
#define UNDELIVERED(flush) \
((flush) == BOUNCE_MSG_FAIL || (flush) == BOUNCE_MSG_WARN)
post_mail_fprintf(bounce, "This is the %s program at host %s.",
var_mail_name, var_myhostname);
post_mail_fputs(bounce, "");
if (bounce_info->flush == BOUNCE_MSG_FAIL) {
post_mail_fputs(bounce,
"I'm sorry to have to inform you that your message could not be");
post_mail_fputs(bounce,
"be delivered to one or more recipients. It's attached below.");
} else if (bounce_info->flush == BOUNCE_MSG_WARN) {
post_mail_fputs(bounce,
"####################################################################");
post_mail_fputs(bounce,
"# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #");
post_mail_fputs(bounce,
"####################################################################");
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce,
"Your message could not be delivered for %.1f hours.",
var_delay_warn_time / 3600.0);
post_mail_fprintf(bounce,
"It will be retried until it is %.1f days old.",
var_max_queue_time / 86400.0);
} else {
post_mail_fputs(bounce,
"Enclosed is the mail delivery report that you requested.");
}
if (UNDELIVERED(bounce_info->flush)) {
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce,
"For further assistance, please send mail to <%s>",
MAIL_ADDR_POSTMASTER);
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce,
"If you do so, please include this problem report. You can");
post_mail_fprintf(bounce,
"delete your own text from the attached returned message.");
}
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
return (vstream_ferror(bounce));
}
static void bounce_print(const char *str, int len, int indent, char *context)
{
VSTREAM *bounce = (VSTREAM *) context;
post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str);
}
static void bounce_print_wrap(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
const char *format,...)
{
va_list ap;
#define LENGTH 79
#define INDENT 4
va_start(ap, format);
vstring_vsprintf(bounce_info->buf, format, ap);
va_end(ap);
line_wrap(STR(bounce_info->buf), LENGTH, INDENT,
bounce_print, (char *) bounce);
}
int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
post_mail_fputs(bounce, "");
if (bounce_info->log_handle->orig_rcpt) {
bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s",
bounce_info->log_handle->recipient,
bounce_info->log_handle->orig_rcpt,
bounce_info->log_handle->text);
} else {
bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
bounce_info->log_handle->recipient,
bounce_info->log_handle->text);
}
return (vstream_ferror(bounce));
}
int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
if (bounce_info->log_handle == 0
|| bounce_log_rewind(bounce_info->log_handle)) {
post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
} else {
while (bounce_log_read(bounce_info->log_handle) != 0)
if (bounce_recipient_log(bounce, bounce_info) != 0)
break;
}
return (vstream_ferror(bounce));
}
int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
post_mail_fprintf(bounce, "Content-Description: %s",
"Delivery report");
post_mail_fprintf(bounce, "Content-Type: %s", "message/delivery-status");
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "Reporting-MTA: dns; %s", var_myhostname);
#if 0
post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
#endif
post_mail_fprintf(bounce, "X-%s-Queue-ID: %s",
bounce_info->mail_name, bounce_info->queue_id);
if (VSTRING_LEN(bounce_info->sender) > 0)
post_mail_fprintf(bounce, "X-%s-Sender: rfc822; %s",
bounce_info->mail_name, STR(bounce_info->sender));
if (bounce_info->arrival_time > 0)
post_mail_fprintf(bounce, "Arrival-Date: %s",
mail_date(bounce_info->arrival_time));
return (vstream_ferror(bounce));
}
int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
bounce_info->log_handle->recipient);
if (bounce_info->log_handle->orig_rcpt) {
xtext_quote(bounce_info->buf, bounce_info->log_handle->orig_rcpt, "+=");
post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s",
STR(bounce_info->buf));
}
post_mail_fprintf(bounce, "Action: %s",
bounce_info->flush == BOUNCE_MSG_FAIL ?
"failed" : bounce_info->log_handle->dsn_action);
post_mail_fprintf(bounce, "Status: %s",
bounce_info->log_handle->dsn_status);
bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
bounce_info->mail_name, bounce_info->log_handle->text);
#if 0
post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
bounce_info->log_handle->log_time);
#endif
if (bounce_info->flush == BOUNCE_MSG_WARN)
post_mail_fprintf(bounce, "Will-Retry-Until: %s",
mail_date(bounce_info->arrival_time + var_max_queue_time));
return (vstream_ferror(bounce));
}
int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
if (bounce_info->log_handle != 0
&& bounce_log_rewind(bounce_info->log_handle) == 0) {
while (bounce_log_read(bounce_info->log_handle) != 0)
if (bounce_recipient_dsn(bounce, bounce_info) != 0)
break;
}
return (vstream_ferror(bounce));
}
int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
int headers_only)
{
int status = 0;
int rec_type = 0;
int bounce_length;
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
post_mail_fprintf(bounce, "Content-Description: %s%s",
UNDELIVERED(bounce_info->flush) ? "Undelivered " : "",
headers_only ? "Message Headers" : "Message");
post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
"text/rfc822-headers" : "message/rfc822");
if (bounce_info->mime_encoding)
post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
bounce_info->mime_encoding);
post_mail_fputs(bounce, "");
if (bounce_info->orig_offs == 0 || vstream_fseek(bounce_info->orig_fp,
bounce_info->orig_offs, SEEK_SET) < 0) {
post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---");
return (vstream_ferror(bounce));
}
#define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s))
bounce_length = 0;
while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) {
if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
break;
if (headers_only && !IS_HEADER(vstring_str(bounce_info->buf)))
break;
if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
bounce_length += VSTRING_LEN(bounce_info->buf) + 2;
status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
} else
break;
}
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary);
return (status);
}
void bounce_delrcpt(BOUNCE_INFO *bounce_info)
{
if (bounce_info->orig_fp != 0
&& bounce_info->log_handle != 0
&& bounce_log_rewind(bounce_info->log_handle) == 0)
while (bounce_log_read(bounce_info->log_handle) != 0)
if (bounce_info->log_handle->rcpt_offset > 0)
deliver_completed(bounce_info->orig_fp,
bounce_info->log_handle->rcpt_offset);
}
void bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
{
if (bounce_info->orig_fp != 0
&& bounce_info->log_handle != 0
&& bounce_info->log_handle->rcpt_offset > 0)
deliver_completed(bounce_info->orig_fp,
bounce_info->log_handle->rcpt_offset);
}