/* This file is part of mailfromd.
Copyright (C) 2005, 2006, 2007, 2008 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#define MF_SOURCE_NAME MF_SOURCE_ENGINE
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
#include <obstack.h>
#include <syslog.h>
#include <signal.h>
#include <pwd.h>
#include <grp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <mailutils/mailutils.h>
#include <mailutils/daemon.h>
#include "mailfromd.h"
static char *ctx_getsym(void *data, char *str);
static int ctx_setreply(void *data, char *code, char *xcode, char *message);
static void ctx_setheader(void *data, struct old_header_node *hdr);
/* Per-message data */
struct smtp_io_data; /* Declared later on */
struct message_data {
eval_environ_t env; /* Evaluation environment */
struct smtp_io_data *io; /* I/O data for SMTP transactions */
mu_list_t hdr; /* List of header modifications */
char *helostr; /* Domain name obtained in HELO phase */
char msgid[64]; /* Message ID */
};
static struct message_data *
priv_get(SMFICTX *ctx)
{
static struct message_data *test_data;
struct message_data *md;
if (mode == MAILFROMD_TEST)
md = test_data;
else
md = (struct message_data*) gacopyz_getpriv(ctx);
if (!md) {
md = malloc(sizeof(*md));
if (!md)
mu_error(_("Not enough memory"));
else {
md->env = create_environment(ctx,
ctx_getsym,
ctx_setreply,
ctx_setheader,
ctx);
clear_rcpt_count(md->env);
md->hdr = NULL;
md->io = NULL;
md->helostr = NULL;
md->msgid[0] = 0;
if (mode == MAILFROMD_TEST)
test_data = md;
else
gacopyz_setpriv(ctx, md);
env_init(md->env);
xeval(md->env, smtp_state_begin);
}
}
if (!md->msgid[0]) {
char *p = gacopyz_getsymval(ctx, "i");
if (p) {
size_t len = strlen(p);
if (len > sizeof md->msgid - 3)
len = sizeof md->msgid - 3;
memcpy(md->msgid, p, len);
md->msgid[len++] = ':';
md->msgid[len++] = ' ';
md->msgid[len] = 0;
}
}
return md;
}
static int
priv_set_io(SMFICTX *ctx, struct smtp_io_data *io)
{
struct message_data *md = priv_get(ctx);
if (!md)
return 1;
md->io = io;
return 0;
}
const char *
mailfromd_msgid(SMFICTX *ctx)
{
struct message_data *md = priv_get(ctx);
return md->msgid;
}
char *
mailfromd_timestr(time_t timestamp, char *timebuf, size_t bufsize)
{
struct tm tm;
gmtime_r(×tamp, &tm);
strftime(timebuf, bufsize, "%c", &tm);
return timebuf;
}
void
destroy_old_header(void *item)
{
struct old_header_node *node = item;
free(node->value);
}
int
priv_store_header_command(SMFICTX *ctx, struct old_header_node *node)
{
struct message_data *md = priv_get(ctx);
if (!md)
return 1;
if (!md->hdr) {
mu_list_create(&md->hdr);
mu_list_set_destroy_item(md->hdr, destroy_old_header);
}
mu_list_append(md->hdr, node);
return 0;
}
/* Run-time execution */
static char *
ctx_getsym(void *data, char *str)
{
char *ret = gacopyz_getsymval(data, str);
if (!ret) {
struct message_data *md = priv_get(data);
if (strcmp (str, "s") == 0)
ret = md->helostr;
}
return ret;
}
static int
ctx_setreply(void *data, char *code, char *xcode, char *message)
{
if (code)
return gacopyz_setreply(data, code, xcode, message);
return 0;
}
static void
ctx_setheader(void *data, struct old_header_node *hdr)
{
priv_store_header_command(data, hdr);
}
/* Message capturing functions */
static int capture_enabled;
void
capture_on()
{
milter_enable_state(smtp_state_helo);
milter_enable_state(smtp_state_envfrom);
milter_enable_state(smtp_state_header);
milter_enable_state(smtp_state_eoh);
milter_enable_state(smtp_state_body);
milter_enable_state(smtp_state_eom);
capture_enabled = 1;
}
static void
capture_from(eval_environ_t env, const char *str)
{
if (capture_enabled) {
time_t t;
struct tm *tm;
char datebuf[26];
env_capture_start(env);
t = time(NULL);
tm = localtime(&t);
mu_strftime(datebuf, sizeof datebuf,
"%a %b %d %H:%M:%S %Y", tm);
env_capture_write_args(env,
"From ", str, " ", datebuf, "\n", NULL);
}
}
static void
capture_header(eval_environ_t env, const char *hf, const char *hv)
{
env_capture_write_args(env, hf, ": ", hv, "\n", NULL);
}
static void
capture_eoh(eval_environ_t env)
{
env_capture_write_args(env, "\n", NULL);
}
static void
capture_body(eval_environ_t env, unsigned char *bodyp, size_t len)
{
env_capture_write(env, bodyp, len);
}
static void
capture_eom(eval_environ_t env)
{
}
/* SMTP I/O functions */
#define SMTP_MAJOR(c) ((c)/100)
struct smtp_io_data {
SMFICTX *ctx; /* Milter context */
mu_stream_t stream; /* I/O stream */
size_t send_off; /* Send offset */
size_t recv_off; /* Receive offset */
struct obstack stk; /* Obstack for keeping commands/replies */
char *command; /* Last issued command */
char *reply; /* Last received reply */
char *start; /* First line of the reply, if it was multiline */
char buf[128]; /* Input buffer */
size_t level; /* Number of bytes in buf */
int code; /* Reply code */
};
void
transcript(struct smtp_io_data *io, char *prefix, const char *msg)
{
if (do_transcript) {
int len = strlen(msg);
if (msg[len-1] == '\n') {
--len;
if (len > 0 && msg[len-1] == '\r')
--len;
}
if (len)
logmsg(LOG_INFO, "%s%s %*.*s",
mailfromd_msgid(io->ctx),
prefix, len, len, msg);
}
}
int
smtp_io_data_init(struct smtp_io_data *dat, SMFICTX *ctx, mu_stream_t stream)
{
obstack_init(&dat->stk);
dat->send_off = dat->recv_off = 0;
dat->stream = stream;
dat->start = dat->command = dat->reply = NULL;
dat->level = 0;
dat->ctx = ctx;
if (ctx && priv_set_io(ctx, dat)) /* save the private data */
return 1;
return 0;
}
void
smtp_io_data_destroy(struct smtp_io_data *dat)
{
if (dat) {
mu_stream_close (dat->stream);
mu_stream_destroy(&dat->stream,
mu_stream_get_owner(dat->stream));
obstack_free(&dat->stk, NULL);
if (dat->ctx)
priv_set_io(dat->ctx, NULL);
}
}
struct timeout_ctl {
time_t start;
time_t timeout;
};
static void
init_timeout_ctl(struct timeout_ctl *tctl, time_t timeout)
{
tctl->start = time(NULL);
tctl->timeout = timeout;
}
#define UPDATE_TTW(t) do { \
time_t now = time(NULL); \
time_t delta = now - (t).start; \
if ((t).timeout > delta) \
(t).timeout -= delta; \
else \
(t).timeout = 0; \
(t).start = now; \
} while (0)
int
smtp_stream_wait(mu_stream_t stream, int flags, struct timeout_ctl *tctl)
{
int rc;
int oflags = flags;
struct timeval tv;
while (tctl->timeout) {
tv.tv_sec = tctl->timeout;
tv.tv_usec = 0;
rc = mu_s
|