#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <mac_expand.h>
#include <split_at.h>
#include <stringops.h>
#include <mymalloc.h>
#include <mail_params.h>
#include <mail_proto.h>
#include <mail_conf.h>
#include <is_header.h>
#include <bounce_template.h>
typedef struct {
const char *suffix;
int suffix_len;
int divisor;
} BOUNCE_TIME_DIVISOR;
#define STRING_AND_LEN(x) (x), (sizeof(x) - 1)
static BOUNCE_TIME_DIVISOR time_divisors[] = {
STRING_AND_LEN("seconds"), 1,
STRING_AND_LEN("minutes"), 60,
STRING_AND_LEN("hours"), 60 * 60,
STRING_AND_LEN("days"), 24 * 60 * 60,
STRING_AND_LEN("weeks"), 7 * 24 * 60 * 60,
0, 0,
};
typedef struct {
const char *param_name;
int param_name_len;
int *value;
} BOUNCE_TIME_PARAMETER;
static BOUNCE_TIME_PARAMETER time_parameter[] = {
STRING_AND_LEN(VAR_DELAY_WARN_TIME), &var_delay_warn_time,
STRING_AND_LEN(VAR_MAX_QUEUE_TIME), &var_max_queue_time,
0, 0,
};
#define STR(x) vstring_str(x)
BOUNCE_TEMPLATE *bounce_template_create(const BOUNCE_TEMPLATE *prototype)
{
BOUNCE_TEMPLATE *tp;
tp = (BOUNCE_TEMPLATE *) mymalloc(sizeof(*tp));
*tp = *prototype;
return (tp);
}
void bounce_template_free(BOUNCE_TEMPLATE *tp)
{
if (tp->buffer) {
myfree(tp->buffer);
myfree((char *) tp->origin);
}
myfree((char *) tp);
}
static void bounce_template_reset(BOUNCE_TEMPLATE *tp)
{
myfree(tp->buffer);
myfree((char *) tp->origin);
*tp = *(tp->prototype);
}
void bounce_template_load(BOUNCE_TEMPLATE *tp, const char *origin,
const char *buffer)
{
if (tp->buffer)
bounce_template_reset(tp);
if (buffer && origin) {
tp->flags |= BOUNCE_TMPL_FLAG_NEW_BUFFER;
tp->buffer = mystrdup(buffer);
tp->origin = mystrdup(origin);
}
}
static void bounce_template_parse_buffer(BOUNCE_TEMPLATE *tp)
{
char *tval = tp->buffer;
char *cp;
char **cpp;
int cpp_len;
int cpp_used;
int hlen;
char *hval;
if ((tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER) == 0)
msg_panic("bounce_template_parse_buffer: nothing to do here");
tp->flags &= ~BOUNCE_TMPL_FLAG_NEW_BUFFER;
#define CLEANUP_AND_RETURN() do { \
bounce_template_reset(tp); \
return; \
} while (0)
#define GETLINE(line, buf) \
(((line) = (buf)) != 0 ? ((buf) = split_at((buf), '\n'), (line)) : 0)
while ((GETLINE(cp, tval)) != 0 && (hlen = is_header(cp)) > 0) {
for (hval = cp + hlen; *hval && (*hval == ':' || ISSPACE(*hval)); hval++)
*hval = 0;
if (*hval == 0) {
msg_warn("%s: empty \"%s\" header value in %s template "
"-- ignoring this template",
tp->origin, cp, tp->class);
CLEANUP_AND_RETURN();
}
if (!allascii(hval)) {
msg_warn("%s: non-ASCII \"%s\" header value in %s template "
"-- ignoring this template",
tp->origin, cp, tp->class);
CLEANUP_AND_RETURN();
}
if (strcasecmp("charset", cp) == 0) {
tp->mime_charset = hval;
} else if (strcasecmp("from", cp) == 0) {
tp->from = hval;
} else if (strcasecmp("subject", cp) == 0) {
tp->subject = hval;
} else if (strcasecmp("postmaster-subject", cp) == 0) {
if (tp->postmaster_subject == 0) {
msg_warn("%s: inapplicable \"%s\" header label in %s template "
"-- ignoring this template",
tp->origin, cp, tp->class);
CLEANUP_AND_RETURN();
}
tp->postmaster_subject = hval;
} else {
msg_warn("%s: unknown \"%s\" header label in %s template "
"-- ignoring this template",
tp->origin, cp, tp->class);
CLEANUP_AND_RETURN();
}
}
while (cp && (*cp == 0 || allspace(cp)))
(void) GETLINE(cp, tval);
if (cp == 0) {
msg_warn("%s: missing message text in %s template "
"-- ignoring this template",
tp->origin, tp->class);
CLEANUP_AND_RETURN();
}
#define NON_ASCII(p) (*(p) && !allascii((p)))
if (NON_ASCII(cp) || NON_ASCII(tval)) {
if (strcasecmp(tp->mime_charset, "us-ascii") == 0) {
msg_warn("%s: 8-bit message text in %s template",
tp->origin, tp->class);
msg_warn("please specify a charset value other than us-ascii");
msg_warn("-- ignoring this template for now");
CLEANUP_AND_RETURN();
}
tp->mime_encoding = MAIL_ATTR_ENC_8BIT;
}
cpp_len = 10;
cpp_used = 0;
cpp = (char **) mymalloc(sizeof(*cpp) * cpp_len);
while (cp) {
cpp[cpp_used++] = cp;
if (cpp_used >= cpp_len) {
cpp = (char **) myrealloc((char *) cpp,
sizeof(*cpp) * 2 * cpp_len);
cpp_len *= 2;
}
(void) GETLINE(cp, tval);
}
cpp[cpp_used] = 0;
tp->message_text = (const char **) cpp;
}
static const char *bounce_template_lookup(const char *key, int unused_mode,
char *context)
{
BOUNCE_TEMPLATE *tp = (BOUNCE_TEMPLATE *) context;
BOUNCE_TIME_PARAMETER *bp;
BOUNCE_TIME_DIVISOR *bd;
static VSTRING *buf;
int result;
for (bp = time_parameter; bp->param_name; bp++) {
if (strncmp(key, bp->param_name, bp->param_name_len) == 0
&& key[bp->param_name_len] == '_') {
for (bd = time_divisors; bd->suffix; bd++) {
if (strcmp(key + bp->param_name_len + 1, bd->suffix) == 0) {
result = bp->value[0] / bd->divisor;
if (result > 999 && bd->divisor < 86400) {
msg_warn("%s: excessive result \"%d\" in %s "
"template conversion of parameter \"%s\"",
tp->origin, result, tp->class, key);
msg_warn("please increase time unit \"%s\" of \"%s\" "
"in %s template", bd->suffix, key, tp->class);
msg_warn("for instructions see the bounce(5) manual");
} else if (result == 0 && bp->value[0] && bd->divisor > 1) {
msg_warn("%s: zero result in %s template "
"conversion of parameter \"%s\"",
tp->origin, tp->class, key);
msg_warn("please reduce time unit \"%s\" of \"%s\" "
"in %s template", bd->suffix, key, tp->class);
msg_warn("for instructions see the bounce(5) manual");
}
if (buf == 0)
buf = vstring_alloc(10);
vstring_sprintf(buf, "%d", result);
return (STR(buf));
}
}
msg_fatal("%s: unrecognized suffix \"%s\" in parameter \"%s\"",
tp->origin,
key + bp->param_name_len + 1, key);
}
}
return (mail_conf_lookup_eval(key));
}
void bounce_template_headers(BOUNCE_XP_PRN_FN out_fn, VSTREAM *fp,
BOUNCE_TEMPLATE *tp,
const char *rcpt,
int postmaster_copy)
{
if (tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER)
bounce_template_parse_buffer(tp);
out_fn(fp, "From: %s", tp->from);
out_fn(fp, "Subject: %s", tp->postmaster_subject && postmaster_copy ?
tp->postmaster_subject : tp->subject);
out_fn(fp, "To: %s", rcpt);
}
void bounce_template_expand(BOUNCE_XP_PUT_FN out_fn, VSTREAM *fp,
BOUNCE_TEMPLATE *tp)
{
VSTRING *buf = vstring_alloc(100);
const char **cpp;
int stat;
const char *filter = "\t !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
if (tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER)
bounce_template_parse_buffer(tp);
for (cpp = tp->message_text; *cpp; cpp++) {
stat = mac_expand(buf, *cpp, MAC_EXP_FLAG_NONE, filter,
bounce_template_lookup, (char *) tp);
if (stat & MAC_PARSE_ERROR)
msg_fatal("%s: bad $name syntax in %s template: %s",
tp->origin, tp->class, *cpp);
if (stat & MAC_PARSE_UNDEF)
msg_fatal("%s: undefined $name in %s template: %s",
tp->origin, tp->class, *cpp);
out_fn(fp, STR(buf));
}
vstring_free(buf);
}
void bounce_template_dump(VSTREAM *fp, BOUNCE_TEMPLATE *tp)
{
const char **cpp;
if (tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER)
bounce_template_parse_buffer(tp);
vstream_fprintf(fp, "Charset: %s\n", tp->mime_charset);
vstream_fprintf(fp, "From: %s\n", tp->from);
vstream_fprintf(fp, "Subject: %s\n", tp->subject);
if (tp->postmaster_subject)
vstream_fprintf(fp, "Postmaster-Subject: %s\n",
tp->postmaster_subject);
vstream_fprintf(fp, "\n");
for (cpp = tp->message_text; *cpp; cpp++)
vstream_fprintf(fp, "%s\n", *cpp);
}