#include <sys_defs.h>
#include <sys/time.h>
#include <unistd.h>
#include <msg.h>
#include <mymalloc.h>
#include <htable.h>
#include <argv.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <iostuff.h>
#include <stringops.h>
#include <mail_proto.h>
#include <cleanup_user.h>
#include <sent.h>
#include <record.h>
#include <rec_type.h>
#include <mark_corrupt.h>
#include <mail_date.h>
#include <mail_params.h>
#include <dsn_mask.h>
#include "local.h"
static HTABLE *forward_dt;
typedef struct FORWARD_INFO {
VSTREAM *cleanup;
char *queue_id;
struct timeval posting_time;
} FORWARD_INFO;
int forward_init(void)
{
if (forward_dt != 0)
msg_panic("forward_init: missing forward_finish call");
forward_dt = htable_create(0);
return (0);
}
static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
{
VSTRING *buffer = vstring_alloc(100);
FORWARD_INFO *info;
VSTREAM *cleanup;
cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING);
if (cleanup == 0)
return (0);
close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC);
if (attr_scan(cleanup, ATTR_FLAG_STRICT,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, buffer,
ATTR_TYPE_END) != 1) {
vstream_fclose(cleanup);
return (0);
}
info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO));
info->cleanup = cleanup;
info->queue_id = mystrdup(STR(buffer));
GETTIMEOFDAY(&info->posting_time);
#define FORWARD_CLEANUP_FLAGS (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL)
attr_print(cleanup, ATTR_FLAG_NONE,
ATTR_TYPE_INT, MAIL_ATTR_FLAGS, FORWARD_CLEANUP_FLAGS,
ATTR_TYPE_END);
rec_fprintf(cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
REC_TYPE_TIME_ARG(info->posting_time));
rec_fputs(cleanup, REC_TYPE_FROM, sender);
if (request->dsn_ret)
rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%d",
MAIL_ATTR_DSN_RET, request->dsn_ret);
if (request->dsn_envid && *(request->dsn_envid))
rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_DSN_ENVID, request->dsn_envid);
#define PASS_ATTR(fp, name, value) do { \
if ((value) && *(value)) \
rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \
} while (0)
PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name);
PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr);
PASS_ATTR(cleanup, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto);
PASS_ATTR(cleanup, MAIL_ATTR_LOG_HELO_NAME, request->client_helo);
PASS_ATTR(cleanup, MAIL_ATTR_SASL_METHOD, request->sasl_method);
PASS_ATTR(cleanup, MAIL_ATTR_SASL_USERNAME, request->sasl_username);
PASS_ATTR(cleanup, MAIL_ATTR_SASL_SENDER, request->sasl_sender);
PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident);
PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context);
vstring_free(buffer);
return (info);
}
int forward_append(DELIVER_ATTR attr)
{
FORWARD_INFO *info;
HTABLE *table_snd;
if (msg_verbose)
msg_info("forward delivered=%s sender=%s recip=%s",
attr.delivered, attr.sender, attr.rcpt.address);
if (forward_dt == 0)
msg_panic("forward_append: missing forward_init call");
if ((table_snd = (HTABLE *) htable_find(forward_dt, attr.delivered)) == 0) {
table_snd = htable_create(0);
htable_enter(forward_dt, attr.delivered, (char *) table_snd);
}
if ((info = (FORWARD_INFO *) htable_find(table_snd, attr.sender)) == 0) {
if ((info = forward_open(attr.request, attr.sender)) == 0)
return (-1);
htable_enter(table_snd, attr.sender, (char *) info);
}
if (*attr.rcpt.dsn_orcpt)
rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_DSN_ORCPT, attr.rcpt.dsn_orcpt);
if (attr.rcpt.dsn_notify)
rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%d",
MAIL_ATTR_DSN_NOTIFY, attr.rcpt.dsn_notify);
if (*attr.rcpt.orig_addr)
rec_fputs(info->cleanup, REC_TYPE_ORCP, attr.rcpt.orig_addr);
rec_fputs(info->cleanup, REC_TYPE_RCPT, attr.rcpt.address);
return (vstream_ferror(info->cleanup));
}
static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
DELIVER_ATTR attr, char *delivered)
{
const char *myname = "forward_send";
VSTRING *buffer = vstring_alloc(100);
int status;
int rec_type = 0;
rec_fputs(info->cleanup, REC_TYPE_MESG, "");
vstring_strcpy(buffer, delivered);
rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)",
var_myhostname, var_mail_name);
rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s",
info->queue_id, mail_date(info->posting_time.tv_sec));
if (local_deliver_hdr_mask & DELIVER_HDR_FWD)
rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s",
lowercase(STR(buffer)));
if ((status = vstream_ferror(info->cleanup)) == 0)
if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
msg_fatal("%s: seek queue file %s: %m:",
myname, VSTREAM_PATH(attr.fp));
while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) {
if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM)
break;
status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type);
}
if (status == 0 && rec_type != REC_TYPE_XTRA) {
msg_warn("%s: bad record type: %d in message content",
info->queue_id, rec_type);
status |= mark_corrupt(attr.fp);
}
if (status == 0) {
rec_fputs(info->cleanup, REC_TYPE_XTRA, "");
rec_fputs(info->cleanup, REC_TYPE_END, "");
}
if (status == 0)
if (vstream_fflush(info->cleanup)
|| attr_scan(info->cleanup, ATTR_FLAG_MISSING,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
ATTR_TYPE_END) != 1)
status = 1;
if (status == 0) {
attr.rcpt.dsn_notify =
(attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ?
DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS);
dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
"forwarded as %s", info->queue_id);
status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr));
}
vstring_free(buffer);
return (status);
}
int forward_finish(DELIVER_REQUEST *request, DELIVER_ATTR attr, int cancel)
{
HTABLE_INFO **dt_list;
HTABLE_INFO **dt;
HTABLE_INFO **sn_list;
HTABLE_INFO **sn;
HTABLE *table_snd;
char *delivered;
char *sender;
FORWARD_INFO *info;
int status = cancel;
if (forward_dt == 0)
msg_panic("forward_finish: missing forward_init call");
for (dt = dt_list = htable_list(forward_dt); *dt; dt++) {
delivered = dt[0]->key;
table_snd = (HTABLE *) dt[0]->value;
for (sn = sn_list = htable_list(table_snd); *sn; sn++) {
sender = sn[0]->key;
info = (FORWARD_INFO *) sn[0]->value;
if (status == 0)
status |= forward_send(info, request, attr, delivered);
if (msg_verbose)
msg_info("forward_finish: delivered %s sender %s status %d",
delivered, sender, status);
(void) vstream_fclose(info->cleanup);
myfree(info->queue_id);
myfree((char *) info);
}
myfree((char *) sn_list);
htable_free(table_snd, (void (*) (char *)) 0);
}
myfree((char *) dt_list);
htable_free(forward_dt, (void (*) (char *)) 0);
forward_dt = 0;
return (status);
}