header_body_checks.c [plain text]
#include <sys_defs.h>
#include <ctype.h>
#include <string.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <mymalloc.h>
#include <mime_state.h>
#include <rec_type.h>
#include <is_header.h>
#include <cleanup_user.h>
#include <dsn_util.h>
#include <header_body_checks.h>
char hbc_checks_error;
const char hbc_checks_unknown;
#define HBC_HEADER_INDEX(class) ((class) - MIME_HDR_FIRST)
#define HBC_BODY_INDEX (0)
#define HBC_INIT(hbc, index, name, value) do { \
HBC_MAP_INFO *_mp = (hbc)->map_info + (index); \
if (*(value) != 0) { \
_mp->map_class = (name); \
_mp->maps = maps_create((name), (value), DICT_FLAG_LOCK); \
} else { \
_mp->map_class = 0; \
_mp->maps = 0; \
} \
} while (0)
#define HBC_CTXT_HEADER "header"
#define HBC_CTXT_BODY "body"
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
static char *hbc_action(void *context, HBC_CALL_BACKS *cb,
const char *map_class, const char *where,
const char *cmd, const char *line,
ssize_t line_len, off_t offset)
{
const char *cmd_args = cmd + strcspn(cmd, " \t");
ssize_t cmd_len = cmd_args - cmd;
char *ret;
while (*cmd_args && ISSPACE(*cmd_args))
cmd_args++;
#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
if (cb->extend
&& (ret = cb->extend(context, cmd, cmd_len, cmd_args, where, line,
line_len, offset)) != HBC_CHECKS_STAT_UNKNOWN)
return (ret);
if (STREQUAL(cmd, "WARN", cmd_len)) {
cb->logger(context, "warning", where, line, cmd_args);
return ((char *) line);
}
if (STREQUAL(cmd, "INFO", cmd_len)) {
cb->logger(context, "info", where, line, cmd_args);
return ((char *) line);
}
if (STREQUAL(cmd, "REPLACE", cmd_len)) {
if (*cmd_args == 0) {
msg_warn("REPLACE action without text in %s map", map_class);
return ((char *) line);
} else if (strcmp(where, HBC_CTXT_HEADER) == 0
&& !is_header(cmd_args)) {
msg_warn("bad REPLACE header text \"%s\" in %s map -- "
"need \"headername: headervalue\"", cmd_args, map_class);
return ((char *) line);
} else {
cb->logger(context, "replace", where, line, cmd_args);
return (mystrdup(cmd_args));
}
}
if (cb->prepend && STREQUAL(cmd, "PREPEND", cmd_len)) {
if (*cmd_args == 0) {
msg_warn("PREPEND action without text in %s map", map_class);
} else if (strcmp(where, HBC_CTXT_HEADER) == 0
&& !is_header(cmd_args)) {
msg_warn("bad PREPEND header text \"%s\" in %s map -- "
"need \"headername: headervalue\"", cmd_args, map_class);
} else {
cb->logger(context, "prepend", where, line, cmd_args);
cb->prepend(context, REC_TYPE_NORM, cmd_args, strlen(cmd_args), offset);
}
return ((char *) line);
}
if (STREQUAL(cmd, "STRIP", cmd_len)) {
cb->logger(context, "strip", where, line, cmd_args);
return (HBC_CHECKS_STAT_IGNORE);
}
if (STREQUAL(cmd, "IGNORE", cmd_len))
return (HBC_CHECKS_STAT_IGNORE);
if (STREQUAL(cmd, "DUNNO", cmd_len)
||STREQUAL(cmd, "OK", cmd_len))
return ((char *) line);
msg_warn("unsupported command in %s map: %s", map_class, cmd);
return ((char *) line);
}
char *hbc_header_checks(void *context, HBC_CHECKS *hbc, int header_class,
const HEADER_OPTS *hdr_opts,
VSTRING *header, off_t offset)
{
const char *myname = "hbc_header_checks";
const char *action;
HBC_MAP_INFO *mp;
if (msg_verbose)
msg_info("%s: '%.30s'", myname, STR(header));
if (hdr_opts && (hdr_opts->flags & HDR_OPT_MIME))
header_class = MIME_HDR_MULTIPART;
mp = hbc->map_info + HBC_HEADER_INDEX(header_class);
if (mp->maps != 0 && (action = maps_find(mp->maps, STR(header), 0)) != 0) {
return (hbc_action(context, hbc->call_backs,
mp->map_class, HBC_CTXT_HEADER, action,
STR(header), LEN(header), offset));
} else if (mp->maps && mp->maps->error) {
return (HBC_CHECKS_STAT_ERROR);
} else {
return (STR(header));
}
}
char *hbc_body_checks(void *context, HBC_CHECKS *hbc, const char *line,
ssize_t len, off_t offset)
{
const char *myname = "hbc_body_checks";
const char *action;
HBC_MAP_INFO *mp;
if (msg_verbose)
msg_info("%s: '%.30s'", myname, line);
mp = hbc->map_info;
if ((action = maps_find(mp->maps, line, 0)) != 0) {
return (hbc_action(context, hbc->call_backs,
mp->map_class, HBC_CTXT_BODY, action,
line, len, offset));
} else if (mp->maps->error) {
return (HBC_CHECKS_STAT_ERROR);
} else {
return ((char *) line);
}
}
HBC_CHECKS *hbc_header_checks_create(const char *header_checks_name,
const char *header_checks_value,
const char *mime_header_checks_name,
const char *mime_header_checks_value,
const char *nested_header_checks_name,
const char *nested_header_checks_value,
HBC_CALL_BACKS *call_backs)
{
HBC_CHECKS *hbc;
if (*header_checks_value == 0 && *mime_header_checks_value == 0
&& *nested_header_checks_value == 0) {
return (0);
} else {
hbc = (HBC_CHECKS *) mymalloc(sizeof(*hbc)
+ (MIME_HDR_LAST - MIME_HDR_FIRST) * sizeof(HBC_MAP_INFO));
hbc->call_backs = call_backs;
HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_PRIMARY),
header_checks_name, header_checks_value);
HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_MULTIPART),
mime_header_checks_name, mime_header_checks_value);
HBC_INIT(hbc, HBC_HEADER_INDEX(MIME_HDR_NESTED),
nested_header_checks_name, nested_header_checks_value);
return (hbc);
}
}
HBC_CHECKS *hbc_body_checks_create(const char *body_checks_name,
const char *body_checks_value,
HBC_CALL_BACKS *call_backs)
{
HBC_CHECKS *hbc;
if (*body_checks_value == 0) {
return (0);
} else {
hbc = (HBC_CHECKS *) mymalloc(sizeof(*hbc));
hbc->call_backs = call_backs;
HBC_INIT(hbc, HBC_BODY_INDEX, body_checks_name, body_checks_value);
return (hbc);
}
}
void _hbc_checks_free(HBC_CHECKS *hbc, ssize_t len)
{
HBC_MAP_INFO *mp;
for (mp = hbc->map_info; mp < hbc->map_info + len; mp++)
if (mp->maps)
maps_free(mp->maps);
myfree((void *) hbc);
}
#ifdef TEST
#include <stdlib.h>
#include <stringops.h>
#include <vstream.h>
#include <msg_vstream.h>
#include <rec_streamlf.h>
#include <mail_params.h>
typedef struct {
HBC_CHECKS *header_checks;
HBC_CHECKS *body_checks;
HBC_CALL_BACKS *call_backs;
VSTREAM *fp;
VSTRING *buf;
const char *queueid;
int recno;
} HBC_TEST_CONTEXT;
#define REC_LEN 1024
static void log_cb(void *context, const char *action, const char *where,
const char *content, const char *text)
{
const HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
if (*text) {
msg_info("%s: %s: %s %.200s: %s",
dp->queueid, action, where, content, text);
} else {
msg_info("%s: %s: %s %.200s",
dp->queueid, action, where, content);
}
}
static void out_cb(void *context, int rec_type, const char *buf,
ssize_t len, off_t offset)
{
const HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
vstream_fwrite(dp->fp, buf, len);
VSTREAM_PUTC('\n', dp->fp);
vstream_fflush(dp->fp);
}
static void head_out(void *context, int header_class,
const HEADER_OPTS *header_info,
VSTRING *buf, off_t offset)
{
HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
char *out;
if (dp->header_checks == 0
|| (out = hbc_header_checks(context, dp->header_checks, header_class,
header_info, buf, offset)) == STR(buf)) {
vstring_sprintf(dp->buf, "%d %s %ld\t|%s",
dp->recno,
header_class == MIME_HDR_PRIMARY ? "MAIN" :
header_class == MIME_HDR_MULTIPART ? "MULT" :
header_class == MIME_HDR_NESTED ? "NEST" :
"ERROR", (long) offset, STR(buf));
out_cb(dp, REC_TYPE_NORM, STR(dp->buf), LEN(dp->buf), offset);
} else if (out != 0) {
vstring_sprintf(dp->buf, "%d %s %ld\t|%s",
dp->recno,
header_class == MIME_HDR_PRIMARY ? "MAIN" :
header_class == MIME_HDR_MULTIPART ? "MULT" :
header_class == MIME_HDR_NESTED ? "NEST" :
"ERROR", (long) offset, out);
out_cb(dp, REC_TYPE_NORM, STR(dp->buf), LEN(dp->buf), offset);
myfree(out);
}
dp->recno += 1;
}
static void head_end(void *context)
{
HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
out_cb(dp, 0, "HEADER END", sizeof("HEADER END") - 1, 0);
}
static void body_out(void *context, int rec_type, const char *buf,
ssize_t len, off_t offset)
{
HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
char *out;
if (dp->body_checks == 0
|| (out = hbc_body_checks(context, dp->body_checks,
buf, len, offset)) == buf) {
vstring_sprintf(dp->buf, "%d BODY %c %ld\t|%s",
dp->recno, rec_type, (long) offset, buf);
out_cb(dp, rec_type, STR(dp->buf), LEN(dp->buf), offset);
} else if (out != 0) {
vstring_sprintf(dp->buf, "%d BODY %c %ld\t|%s",
dp->recno, rec_type, (long) offset, out);
out_cb(dp, rec_type, STR(dp->buf), LEN(dp->buf), offset);
myfree(out);
}
dp->recno += 1;
}
static void body_end(void *context)
{
HBC_TEST_CONTEXT *dp = (HBC_TEST_CONTEXT *) context;
out_cb(dp, 0, "BODY END", sizeof("BODY END") - 1, 0);
}
static void err_print(void *unused_context, int err_flag,
const char *text, ssize_t len)
{
msg_warn("%s: %.*s", mime_state_error(err_flag),
len < 100 ? (int) len : 100, text);
}
int var_header_limit = 2000;
int var_mime_maxdepth = 20;
int var_mime_bound_len = 2000;
char *var_drop_hdrs = DEF_DROP_HDRS;
int main(int argc, char **argv)
{
int rec_type;
VSTRING *buf;
int err;
MIME_STATE *mime_state;
HBC_TEST_CONTEXT context;
static HBC_CALL_BACKS call_backs[1] = {
log_cb,
out_cb,
};
if (argc != 5)
msg_fatal("usage: %s header_checks mime_header_checks nested_header_checks body_checks", argv[0]);
#define MIME_OPTIONS \
(MIME_OPT_REPORT_8BIT_IN_7BIT_BODY \
| MIME_OPT_REPORT_8BIT_IN_HEADER \
| MIME_OPT_REPORT_ENCODING_DOMAIN \
| MIME_OPT_REPORT_TRUNC_HEADER \
| MIME_OPT_REPORT_NESTING \
| MIME_OPT_DOWNGRADE)
msg_vstream_init(basename(argv[0]), VSTREAM_OUT);
buf = vstring_alloc(10);
mime_state = mime_state_alloc(MIME_OPTIONS,
head_out, head_end,
body_out, body_end,
err_print,
(void *) &context);
context.header_checks =
hbc_header_checks_create("header_checks", argv[1],
"mime_header_checks", argv[2],
"nested_header_checks", argv[3],
call_backs);
context.body_checks =
hbc_body_checks_create("body_checks", argv[4], call_backs);
context.buf = vstring_alloc(100);
context.fp = VSTREAM_OUT;
context.queueid = "test-queueID";
context.recno = 0;
do {
rec_type = rec_streamlf_get(VSTREAM_IN, buf, REC_LEN);
VSTRING_TERMINATE(buf);
err = mime_state_update(mime_state, rec_type, STR(buf), LEN(buf));
vstream_fflush(VSTREAM_OUT);
} while (rec_type > 0);
if (err & MIME_ERR_TRUNC_HEADER)
msg_warn("message header length exceeds safety limit");
if (err & MIME_ERR_NESTING)
msg_warn("MIME nesting exceeds safety limit");
if (err & MIME_ERR_8BIT_IN_HEADER)
msg_warn("improper use of 8-bit data in message header");
if (err & MIME_ERR_8BIT_IN_7BIT_BODY)
msg_warn("improper use of 8-bit data in message body");
if (err & MIME_ERR_ENCODING_DOMAIN)
msg_warn("improper message/* or multipart/* encoding domain");
if (context.header_checks)
hbc_header_checks_free(context.header_checks);
if (context.body_checks)
hbc_body_checks_free(context.body_checks);
vstring_free(context.buf);
mime_state_free(mime_state);
vstring_free(buf);
exit(0);
}
#endif