bounce_notify_util.c [plain text]
#include <sys_defs.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.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 <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 <dsn_mask.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,
const char *dsn_envid,
RCPT_BUF *rcpt_buf,
DSN_BUF *dsn_buf,
BOUNCE_TEMPLATE *template,
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;
}
if (dsn_envid && *dsn_envid)
bounce_info->dsn_envid = dsn_envid;
else
bounce_info->dsn_envid = 0;
bounce_info->template = template;
bounce_info->buf = vstring_alloc(100);
bounce_info->sender = vstring_alloc(100);
bounce_info->arrival_time = 0;
bounce_info->orig_offs = 0;
bounce_info->message_size = 0;
bounce_info->rcpt_buf = rcpt_buf;
bounce_info->dsn_buf = dsn_buf;
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_SIZE) {
if (bounce_info->message_size == 0)
sscanf(STR(bounce_info->buf), "%ld %ld",
&bounce_info->message_size,
&bounce_info->orig_offs);
if (bounce_info->message_size < 0)
bounce_info->message_size = 0;
if (bounce_info->orig_offs < 0)
bounce_info->orig_offs = 0;
}
else if (rec_type == REC_TYPE_TIME) {
if (bounce_info->arrival_time == 0
&& (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;
}
if (bounce_info->orig_offs > 0
&& bounce_info->arrival_time > 0
&& VSTRING_LEN(bounce_info->sender) > 0)
break;
}
}
return (bounce_info);
}
BOUNCE_INFO *bounce_mail_init(const char *service,
const char *queue_name,
const char *queue_id,
const char *encoding,
const char *dsn_envid,
BOUNCE_TEMPLATE *template)
{
BOUNCE_INFO *bounce_info;
BOUNCE_LOG *log_handle;
RCPT_BUF *rcpt_buf;
DSN_BUF *dsn_buf;
if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0) {
if (errno != ENOENT)
msg_fatal("open %s %s: %m", service, queue_id);
rcpt_buf = 0;
dsn_buf = 0;
} else {
rcpt_buf = rcpb_create();
dsn_buf = dsb_create();
}
bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding,
dsn_envid, rcpt_buf, dsn_buf,
template, log_handle);
return (bounce_info);
}
BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
const char *queue_id,
const char *encoding,
const char *dsn_envid,
RCPT_BUF *rcpt_buf,
DSN_BUF *dsn_buf,
BOUNCE_TEMPLATE *template)
{
BOUNCE_INFO *bounce_info;
bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding,
dsn_envid, rcpt_buf, dsn_buf, template,
(BOUNCE_LOG *) 0);
return (bounce_info);
}
void bounce_mail_free(BOUNCE_INFO *bounce_info)
{
if (bounce_info->log_handle) {
if (bounce_log_close(bounce_info->log_handle))
msg_warn("%s: read bounce log %s: %m",
bounce_info->queue_id, bounce_info->queue_id);
rcpb_free(bounce_info->rcpt_buf);
dsb_free(bounce_info->dsn_buf);
}
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, int postmaster_copy)
{
BOUNCE_TEMPLATE *template = bounce_info->template;
#define STREQ(a, b) (strcasecmp((a), (b)) == 0)
bounce_template_headers(post_mail_fprintf, bounce, template,
STR(quote_822_local(bounce_info->buf, dest)),
postmaster_copy);
post_mail_fprintf(bounce, "Auto-Submitted: %s", postmaster_copy ?
"auto-generated" : "auto-replied");
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",
STREQ(bounce_info->mime_encoding, MAIL_ATTR_ENC_7BIT) ?
bounce_template_encoding(template) :
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; charset=%s",
"text/plain", bounce_template_charset(template));
post_mail_fputs(bounce, "");
return (vstream_ferror(bounce));
}
int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
{
bounce_template_expand(post_mail_fputs, bounce, bounce_info->template);
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)
{
RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
DSN *dsn = &bounce_info->dsn_buf->dsn;
#define NON_NULL_EMPTY(s) ((s) && *(s))
post_mail_fputs(bounce, "");
if (NON_NULL_EMPTY(rcpt->orig_addr)) {
bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s",
rcpt->address, rcpt->orig_addr, dsn->reason);
} else {
bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
rcpt->address, dsn->reason);
}
return (vstream_ferror(bounce));
}
int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
int notify_filter)
{
RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
int count = 0;
if (bounce_info->log_handle == 0
|| bounce_log_rewind(bounce_info->log_handle)) {
if (IS_FAILURE_TEMPLATE(bounce_info->template)) {
post_mail_fputs(bounce, "");
post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
count = 1;
}
} else {
while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
bounce_info->dsn_buf) != 0) {
if (rcpt->dsn_notify == 0
|| (rcpt->dsn_notify & notify_filter)) {
count++;
if (bounce_recipient_log(bounce, bounce_info) != 0)
break;
}
}
}
return (vstream_ferror(bounce) ? -1 : count);
}
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
if (NON_NULL_EMPTY(bounce_info->dsn_envid)) {
post_mail_fprintf(bounce, "Original-Envelope-Id: %s",
bounce_info->dsn_envid);
}
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)
{
RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
DSN *dsn = &bounce_info->dsn_buf->dsn;
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", rcpt->address);
if (NON_NULL_EMPTY(rcpt->dsn_orcpt)) {
post_mail_fprintf(bounce, "Original-Recipient: %s", rcpt->dsn_orcpt);
} else if (NON_NULL_EMPTY(rcpt->orig_addr)) {
post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s",
rcpt->orig_addr);
}
post_mail_fprintf(bounce, "Action: %s",
IS_FAILURE_TEMPLATE(bounce_info->template) ?
"failed" : dsn->action);
post_mail_fprintf(bounce, "Status: %s", dsn->status);
if (NON_NULL_EMPTY(dsn->mtype) && NON_NULL_EMPTY(dsn->mname))
bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s",
dsn->mtype, dsn->mname);
if (NON_NULL_EMPTY(dsn->dtype) && NON_NULL_EMPTY(dsn->dtext))
bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s",
dsn->dtype, dsn->dtext);
else
bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
bounce_info->mail_name, dsn->reason);
#if 0
if (dsn->time > 0)
post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
mail_date(dsn->time));
#endif
if (IS_DELAY_TEMPLATE(bounce_info->template))
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,
int notify_filter)
{
RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
int count = 0;
if (bounce_info->log_handle == 0
|| bounce_log_rewind(bounce_info->log_handle)) {
if (IS_FAILURE_TEMPLATE(bounce_info->template))
count = 1;
} else {
while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
bounce_info->dsn_buf) != 0) {
if (rcpt->dsn_notify == 0
|| (rcpt->dsn_notify & notify_filter)) {
count++;
if (bounce_recipient_dsn(bounce, bounce_info) != 0)
break;
}
}
}
return (vstream_ferror(bounce) ? -1 : count);
}
int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
int headers_only)
{
int status = 0;
int rec_type = 0;
if (var_bounce_limit > 0
&& bounce_info->orig_fp
&& (bounce_info->message_size <= 0
|| bounce_info->message_size > var_bounce_limit))
headers_only = DSN_RET_HDRS;
#define IS_UNDELIVERED_TEMPLATE(template) \
(IS_FAILURE_TEMPLATE(template) || IS_DELAY_TEMPLATE(template))
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
post_mail_fprintf(bounce, "Content-Description: %s%s",
IS_UNDELIVERED_TEMPLATE(bounce_info->template) ?
"Undelivered " : "",
headers_only == DSN_RET_HDRS ?
"Message Headers" : "Message");
post_mail_fprintf(bounce, "Content-Type: %s",
headers_only == DSN_RET_HDRS ?
"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))
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 == DSN_RET_HDRS
&& !IS_HEADER(vstring_str(bounce_info->buf)))
break;
status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
}
post_mail_fputs(bounce, "");
post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary);
return (status);
}
void bounce_delrcpt(BOUNCE_INFO *bounce_info)
{
RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
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, bounce_info->rcpt_buf,
bounce_info->dsn_buf) != 0)
if (rcpt->offset > 0)
deliver_completed(bounce_info->orig_fp, rcpt->offset);
}
void bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
{
RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
if (bounce_info->orig_fp != 0 && rcpt->offset > 0)
deliver_completed(bounce_info->orig_fp, rcpt->offset);
}