/*
** Copyright (c) 2001-2002 The OSSP Project
** Copyright (c) 2001-2002 Cable & Wireless Deutschland
**
** This file is part of OSSP lmtp2nntp, an LMTP speaking local
** mailer which forwards mails as Usenet news articles via NNTP.
** It can be found at http://www.ossp.org/pkg/lmtp2nntp/.
**
** 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
** 2.0 of the License, 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 file; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA, or contact the OSSP project .
**
** lmtp2nntp_config.c: config handling
*/
#include
#include
#include
#include
#include
/* third party (included) */
#include "lmtp2nntp_argz.h"
/* third party (linked in) */
#include "l2.h"
#include "pcre.h"
#include "popt.h"
#include "str.h"
#include "var.h"
#include "val.h"
/* library version check (compile-time) */
#define STR_VERSION_HEX_REQ 0x009206
#define STR_VERSION_STR_REQ "0.9.6"
#ifdef STR_VERSION_HEX
#if STR_VERSION_HEX < STR_VERSION_HEX_REQ
#error "require a newer version of OSSP Str"
#endif
#endif
/* own headers */
#include "lmtp2nntp_global.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(HAVE_DMALLOC_H) && defined(DMALLOC)
#include "dmalloc.h"
#endif
#include "lmtp2nntp_option.h"
#include "lmtp2nntp_config.h"
#include "lmtp2nntp_lmtp.h"
#include "lmtp2nntp_nntp.h"
#include "lmtp2nntp_msg.h"
#include "fixme.h"
#define _LMTP2NNTP_VERSION_C_AS_HEADER_
#include "lmtp2nntp_version.c"
#undef _LMTP2NNTP_VERSION_C_AS_HEADER_
#ifndef FALSE
#define FALSE (1 != 1)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#ifndef NUL
#define NUL '\0'
#endif
static l2_result_t
formatter_prefix(l2_context_t *_ctx, const char id, const char *param,
char *bufptr, size_t bufsize, size_t *buflen, va_list *ap)
{
lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx->vp;
if ((ctx->msg != NULL) && (ctx->msg->cpFid != NULL)) {
sprintf(bufptr, "%s: ", ctx->msg->cpFid);
*buflen = strlen(bufptr);
}
else
*buflen = 0;
return L2_OK;
}
static l2_result_t
formatter_errno(l2_context_t *_ctx, const char id, const char *param,
char *bufptr, size_t bufsize, size_t *buflen, va_list *ap)
{
sprintf(bufptr, "(%d) %s", errno, strerror(errno));
*buflen = strlen(bufptr);
return L2_OK;
}
static var_syntax_t syntax_regex = {
'\\', /* escape */
'$', /* varinit */
'{', /* startdelim */
'}', /* enddelim */
NUL, /* startindex */
NUL, /* endindex */
NUL, /* current_index */
"0-9" /* namechars */
};
void config_context(lmtp2nntp_t *ctx)
{
ex_t ex;
optionval_t *ov;
//char *cp;
int rc;
/* create L2 environment */
if (l2_env_create(&ctx->l2_env) != L2_OK) {
fprintf(stderr, "%s:Error: failed to create L2 environment\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (l2_env_formatter(ctx->l2_env, 'P', formatter_prefix, &ctx->ctx) != L2_OK) {
fprintf(stderr, "%s:Error: logging failed to register prefix formatter\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (l2_env_formatter(ctx->l2_env, 'D', l2_util_fmt_dump, NULL) != L2_OK) {
fprintf(stderr, "%s:Error: logging failed to register dump formatter\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (l2_env_formatter(ctx->l2_env, 'S', l2_util_fmt_string, NULL) != L2_OK) {
fprintf(stderr, "%s:Error: logging failed to register string formatter\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (l2_env_formatter(ctx->l2_env, 'm', formatter_errno, NULL) != L2_OK) {
fprintf(stderr, "%s:Error: logging failed to register errno formatter\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (val_get(ctx->val, "option.l2spec", &ov) != VAL_OK) {
fprintf(stderr, "%s:Error: (internal) config did not register 'l2spec' option\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (ov->data.s != NULL) {
//FIXME this is cut off on command line!? fprintf(stderr, "DEBUG: ov->data.s = \"%s\"\n", ov->data.s);
if ((rc = l2_spec(&ctx->l2, ctx->l2_env, ov->data.s)) != L2_OK) {
fprintf(stderr, "%s:Error: logging failed to create stream\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (l2_channel_levels(ctx->l2, L2_LEVEL_ALL, L2_LEVEL_NONE) != L2_OK) { /* FIXME should this globalmask and flushmask be user-configurable? */
fprintf(stderr, "%s:Error: logging failed to set global logging level\n", ctx->progname);
CU(ERR_EXECUTION);
}
if (l2_channel_open(ctx->l2) != L2_OK) {
fprintf(stderr, "%s:Error: logging failed to open channel stream\n", ctx->progname);
CU(ERR_EXECUTION);
}
}
/* from this point on logging is up and running and fprintf(stderr, ...)
* should not be used in the remainder of the program flow.
*/
log1(ctx, NOTICE, "startup, version %s", lmtp2nntp_version.v_gnu);
/* --version FLAG */
try {
if ( (val_get(ctx->val, "option.version", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.f < 0)
|| (ov->ndata == 1 && ov->data.f > 1)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--version = %d", ov->data.f);
if (ov->data.f == 1) {
fprintf(stdout, "%s\n", lmtp2nntp_version.v_gnu);
exit(0); //FIXME
}
}
catch (ex)
rethrow;
/* --childsmax SINGLE */
try {
if ( (val_get(ctx->val, "option.childsmax", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--childsmax = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_childsmax = atoi(ov->data.s)) <= 0) {
log1(ctx, ERROR, "option --childsmax, number (%d) out of range", ctx->option_childsmax);
throw(0,0,0);
}
}
catch (ex) {
rethrow;
}
/* --daemonize FLAG */
try {
if ( (val_get(ctx->val, "option.daemonize", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.f < 0)
|| (ov->ndata == 1 && ov->data.f > 1)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--daemonize = %d", ov->data.f);
ctx->option_daemon = ov->data.f == 1 ? TRUE : FALSE;
}
catch (ex)
rethrow;
/* --kill FLAG */
try {
if ( (val_get(ctx->val, "option.kill", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.f < 0)
|| (ov->ndata == 1 && ov->data.f > 1)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--kill = %d", ov->data.f);
ctx->option_killflag = ov->data.f == 1 ? TRUE : FALSE;
}
catch (ex)
rethrow;
/* --pidfile SINGLE */
try {
if ( (val_get(ctx->val, "option.pidfile", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--pidfile = \"%s\"", ov->data.s);
if (ov->ndata == 1)
ctx->option_pidfile = ov->data.s;
}
catch (ex)
rethrow;
/* --acl MULTI */
try {
char *cp;
int i;
int somepass;
if ( (val_get(ctx->val, "option.acl", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata >= 1 && ov->data.m == NULL)
) throw(0,0,0);
log1(ctx, DEBUG, "ov->ndata = %d", ov->ndata);
for (i = 0; i < ov->ndata; i++)
log2(ctx, TRACE, "--acl[%d] = \"%s\"", i, (ov->data.m)[i]);
/* check if only blocking ACLs exist */
somepass = FALSE;
if (ov->ndata >= 1) {
for (i = 0; i < ov->ndata; i++) {
cp = (ov->data.m)[i];
if (cp[0] != '!') {
somepass = TRUE;
break;
}
}
}
/* if only blocking ACLs exist, reserve space for two additional pass-through wildcards */
if ((ctx->pacl = (struct acl *)malloc((ov->ndata + somepass ? 0 : 2 ) * sizeof(struct acl))) == NULL) throw(0,0,0);
if (ov->ndata >= 1) {
for (i = 0; i < ov->ndata; i++) {
cp = (ov->data.m)[i];
log2(ctx, DEBUG, "cp = (data.m)[%d] = \"%s\"", i, cp);
if (cp[0] == '!') {
ctx->pacl[i].acl = strdup(cp + 1);
ctx->pacl[i].not = TRUE;
}
else {
ctx->pacl[i].acl = strdup(cp);
ctx->pacl[i].not = FALSE;
}
log2(ctx, DEBUG, "ctx->pacl[%d].not = %s", i, ctx->pacl[i].not == TRUE ? "TRUE" : "FALSE");
log2(ctx, DEBUG, "ctx->pacl[%d].acl = %s", i, ctx->pacl[i].acl);
if ((cp = strrchr(ctx->pacl[i].acl, '/')) != NULL)
*cp++ = NUL;
else
cp = "-1";
ctx->pacl[i].prefixlen = atoi(cp);
log2(ctx, DEBUG, "ctx->pacl[%d].prefixlen = %d", i, ctx->pacl[i].prefixlen);
if ((rc = sa_addr_create(&(ctx->pacl[i].saa))) != SA_OK) {
log1(ctx, ERROR, "option --acl, create address (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_addr_u2a(ctx->pacl[i].saa, "inet://%s:0", ctx->pacl[i].acl)) != SA_OK) {
log2(ctx, ERROR, "option --acl, parsing address (%s) failed with \"%s\"", ctx->pacl[i].acl, sa_error(rc));
throw(0,0,0);
}
}
ctx->nacl = i;
}
/* if only blocking ACLs exist, append a wildcard pass-through for IPv4 */
if (!somepass) {
i = ctx->nacl;
ctx->pacl[i].acl = "0.0.0.0";
ctx->pacl[i].not = FALSE;
ctx->pacl[i].prefixlen = 0;
if ((rc = sa_addr_create(&ctx->pacl[i].saa)) != SA_OK) {
log1(ctx, ERROR, "option --acl, create IPv4 pass-through address (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_addr_u2a(ctx->pacl[i].saa, "inet://%s:0", ctx->pacl[i].acl)) != SA_OK) {
log2(ctx, ERROR, "option --acl, parsing IPv4 pass-through address (%s) failed with \"%s\"", ctx->pacl[i].acl, sa_error(rc));
throw(0,0,0);
}
i++;
ctx->nacl = i;
}
/* if only blocking ACLs exist, append a wildcard pass-through for IPv6 */
if (!somepass) {
i = ctx->nacl;
ctx->pacl[i].acl = "[::]";
ctx->pacl[i].not = FALSE;
ctx->pacl[i].prefixlen = 0;
if ((rc = sa_addr_create(&ctx->pacl[i].saa)) != SA_OK) {
log1(ctx, ERROR, "option --acl, create IPv6 pass-through address (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_addr_u2a(ctx->pacl[i].saa, "inet://%s:0", ctx->pacl[i].acl)) != SA_OK) {
log2(ctx, ERROR, "option --acl, parsing IPv6 pass-through address (%s) failed with \"%s\"", ctx->pacl[i].acl, sa_error(rc));
throw(0,0,0);
}
i++;
ctx->nacl = i;
}
}
catch (ex)
rethrow;
/* --bind SINGLE */
try {
if ( (val_get(ctx->val, "option.bind", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--bind = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
/* dash means stdio */
if (strcmp(ov->data.s, "-") != 0) {
if ((rc = sa_create(&ctx->saServerbind)) != SA_OK) {
log1(ctx, ERROR, "option --bind, creating TCP socket (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_addr_create(&ctx->saaServerbind)) != SA_OK) {
log1(ctx, ERROR, "option --bind, create address (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
/* slash means UNIX socket */
if (ov->data.s[0] == '/') {
char *cpPath;
char *cpPerm;
int nPerm;
int n;
int i;
cpPath = strdup(ov->data.s);
cpPerm = NULL;
nPerm = -1;
if ((cpPerm = strrchr(cpPath, ':')) != NULL) {
*cpPerm++ = '\0';
nPerm = 0;
for (i = 0; i < 4 && cpPerm[i] != '\0'; i++) {
if (!isdigit((int)cpPerm[i])) {
nPerm = -1;
break;
}
n = cpPerm[i] - '0';
if (n > 7) {
nPerm = -1;
break;
}
nPerm = ((nPerm << 3) | n);
}
if (nPerm == -1 || cpPerm[i] != '\0') {
log1(ctx, ERROR, "option --bind, invalid permissions \"%s\"", cpPerm);
throw(0,0,0);
}
}
if ((rc = sa_addr_u2a(ctx->saaServerbind, "unix:%s", cpPath)) != SA_OK) {
log2(ctx, ERROR, "option --bind, parsing alternate IO guessing UNIX socket (%s) failed with \"%s\"", cpPath, sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_bind(ctx->saServerbind, ctx->saaServerbind)) != SA_OK) {
log2(ctx, ERROR, "option --bind, bind (%s) failed with \"%s\"", cpPath, sa_error(rc));
throw(0,0,0);
}
if (nPerm != -1) {
if (chmod(cpPath, nPerm) == -1) {
log3(ctx, ERROR, "option --bind, chmod (%s, 0%o) failed with \"%s\"", cpPath, nPerm, strerror(errno));
throw(0,0,0);
}
}
if (getuid() == 0 && getuid() != ctx->option_uid) {
if (chown(cpPath, ctx->option_uid, -1) == -1) {
log3(ctx, ERROR, "option --bind, chown (%s, %d) failed with \"%s\"", cpPath, ctx->option_uid, strerror(errno));
throw(0,0,0);
}
}
free(cpPath);
}
/* otherwise assume INET socket */
else {
if ((rc = sa_addr_u2a(ctx->saaServerbind, "inet://%s", ov->data.s)) != SA_OK) {
log2(ctx, ERROR, "option --bind, parsing alternate IO guessing INET socket (%s) failed with \"%s\"", ov->data.s, sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_bind(ctx->saServerbind, ctx->saaServerbind)) != SA_OK) {
log2(ctx, ERROR, "option --bind, bind (%s) failed with \"%s\"", ov->data.s, sa_error(rc));
throw(0,0,0);
}
}
/* for either sockets */
if ((rc = sa_listen(ctx->saServerbind, -1)) != SA_OK) {
log2(ctx, ERROR, "option --bind, listen (%s) failed with \"%s\"", ov->data.s, sa_error(rc));
throw(0,0,0);
}
}
}
}
catch (ex)
rethrow;
/* --client SINGLE */
try {
if ( (val_get(ctx->val, "option.client", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--client = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
if ((rc = sa_addr_create(&ctx->saaClientbind)) != SA_OK) {
log1(ctx, ERROR, "option --client, create address (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_addr_u2a(ctx->saaClientbind,
(strchr(ov->data.s, ':') == NULL) ? "inet://%s:0" : "inet://%s",
ov->data.s)) != SA_OK) {
log2(ctx, ERROR, "option --client, parsing alternate IO guessing INET socket (%s) failed with \"%s\"", ov->data.s, sa_error(rc));
throw(0,0,0);
}
}
}
catch (ex)
rethrow;
/* --destination MULTI */
try {
char *cp;
int i;
if ( (val_get(ctx->val, "option.destination", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata >= 1 && ov->data.m == NULL)
) throw(0,0,0);
log1(ctx, DEBUG, "ov->ndata = %d", ov->ndata);
for (i = 0; i < ov->ndata; i++)
log2(ctx, TRACE, "--destination[%d] = \"%s\"", i, (ov->data.m)[i]);
if (ov->ndata >= 1) {
if ((ctx->pns = (struct ns *)malloc(ov->ndata * sizeof(struct ns))) == NULL) throw(0,0,0);
for (i = 0; i < ov->ndata;) {
cp = (ov->data.m)[i];
log2(ctx, DEBUG, "cp = (data.m)[%d] = \"%s\"", i, cp);
if (strrchr(cp, ':') == NULL)
cp = str_concat(cp, ":nntp", NULL); //FIXME is this a config var/val?
else
cp = str_concat(cp, NULL); /* prepare for free() */
if ((rc = sa_addr_create(&ctx->pns[i].saa)) != SA_OK) {
log1(ctx, ERROR, "option --destination, create address (internal) failed with \"%s\"", sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_addr_u2a(ctx->pns[i].saa, "inet://%s", cp)) != SA_OK) {
log2(ctx, ERROR, "option --destination, parsing host address (%s) failed with \"%s\"", cp /*FIXME again, option vs. config */, sa_error(rc));
throw(0,0,0);
}
if ((rc = sa_create(&ctx->pns[i].sa)) != SA_OK) {
log2(ctx, ERROR, "option --destination, creating TCP socket (%s) failed with \"%s\"", cp /*FIXME again, option vs. config */, sa_error(rc));
throw(0,0,0);
}
ctx->pns[i].nntp = NULL;
ctx->pns[i].rc = NNTP_OK;
ctx->pns[i].l2 = ctx->l2;
free(cp);
ctx->nns = ++i;
}
}
}
catch (ex)
rethrow;
/* --groupmode SINGLE */
try {
if ( (val_get(ctx->val, "option.groupmode", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--groupmode = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
if (strcasecmp(ov->data.s, "arg") == 0)
ctx->option_groupmode = GROUPMODE_ARG;
else if (strcasecmp(ov->data.s, "envelope") == 0)
ctx->option_groupmode = GROUPMODE_ENVELOPE;
else if (strcasecmp(ov->data.s, "header") == 0)
ctx->option_groupmode = GROUPMODE_HEADER;
else {
log1(ctx, ERROR, "option --groupmode, invalid mode (%s)", ov->data.s);
throw(0,0,0);
}
}
}
catch (ex)
rethrow;
/* --headerrule MULTI */
{
volatile headerrule_t *hrNew = NULL; // declare and initialize variables which might have resources allocated that need to be cleaned up when an exception is caught
try {
char *cp, *cpP;
int n;
int i;
headerrule_t *hrI;
headerrule_t *hrP;
const char *cpError;
int iError;
if ( (val_get(ctx->val, "option.headerrule", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata >= 1 && ov->data.m == NULL)
) throw(0,0,0);
log1(ctx, DEBUG, "ov->ndata = %d", ov->ndata);
for (i = 0; i < ov->ndata; i++)
log2(ctx, TRACE, "--headerule[%d] = \"%s\"", i, (ov->data.m)[i]);
if (ov->ndata >= 1) {
for (i = 0; i < ov->ndata; i++) {
cp = (ov->data.m)[i];
log2(ctx, DEBUG, "cp = (data.m)[%d] = \"%s\"", i, cp);
hrNew = (headerrule_t *)mallocex(sizeof(headerrule_t));
hrNew->next = NULL;
hrNew->pri = 500; /* default priority */
hrNew->regex = NULL;
hrNew->header = NULL;
hrNew->val = NULL;
hrNew->pcreRegex = NULL;
hrNew->pcreExtra = NULL;
/* priority */
cpP = cp;
if ((cp = strchr(cp, ':')) == NULL) {
log1(ctx, ERROR, "option --headerrule, priority (%s) terminating colon missing", (ov->data.m)[i]);
throw(0,0,0);
}
cp++;
n = cp - cpP;
if (n >= 2) /* mandatory colon and at least one more char */
hrNew->pri = atoi(cpP);
/* regex */
cpP = cp;
if ((cp = strchr(cp, ':')) == NULL) {
log1(ctx, ERROR, "option --headerrule, regex (%s) terminating colon missing", (ov->data.m)[i]);
throw(0,0,0);
}
cp++;
n = cp - cpP;
if (n >= 2) /* mandatory colon and at least one more char */
hrNew->regex = str_dupex(cpP, n);
/* header */
cpP = cp;
if ((cp = strchr(cp, ':')) == NULL) {
log1(ctx, ERROR, "option --headerrule, header (%s) terminating colon missing", (ov->data.m)[i]);
throw(0,0,0);
}
cp++;
n = cp - cpP;
if (n == 0) {
log1(ctx, ERROR, "option --headerrule, header (%s) missing", (ov->data.m)[i]);
throw(0,0,0);
}
hrNew->header = str_dupex(cpP, n);
/* value */
cpP = cp;
n = strlen(cpP);
if (n >= 1)
hrNew->val = str_dupex(cpP, n);
if (hrNew->regex != NULL) {
/* compile regular expression into finite state machine and optimize */
if ((hrNew->pcreRegex = pcre_compile(hrNew->regex, PCRE_CASELESS, &cpError, &iError, NULL)) == NULL) {
log3(ctx, ERROR, "option --headerrule, regex (%s) failed at pos %d with %s", hrNew->regex, iError, cpError);
throw(0,0,0);
}
hrNew->pcreExtra = pcre_study(hrNew->pcreRegex, 0, &cpError);
if (cpError != NULL) {
log1(ctx, ERROR, "option --headerrule, regex optimization failed with %s", cpError);
throw(0,0,0);
}
}
if (ctx->option_firstheaderrule == NULL)
ctx->option_firstheaderrule = (headerrule_t *)hrNew; /* first */
else {
for (hrP = NULL, hrI = ctx->option_firstheaderrule;
hrI != NULL && hrI->pri <= hrNew->pri;
hrP = hrI, hrI = hrI->next);
if (hrI != NULL)
hrNew->next = hrI; /* insert */
if (hrP != NULL)
hrP->next = (headerrule_t *)hrNew; /* append */
else
ctx->option_firstheaderrule = (headerrule_t *)hrNew; /* new first */
}
hrNew = NULL; /* release cleanup responsibility */
}
/* establish variable expansion context */
{
var_rc_t rc;
if ((rc = var_create(&ctx->config_varregex)) != VAR_OK) {
log2(ctx, ERROR, "option --headerrule, create regex context failed with %s (%d)", var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
throw(0,0,0);
}
if ((rc = var_config(ctx->config_varregex, VAR_CONFIG_SYNTAX, &syntax_regex)) != VAR_OK) {
log2(ctx, ERROR, "option --headerrule, config regex context failed with %s (%d)", var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
throw(0,0,0);
}
}
}
}
cleanup {
if (hrNew != NULL) {
if (hrNew->pcreExtra != NULL)
free(hrNew->pcreExtra);
if (hrNew->pcreRegex != NULL)
free(hrNew->pcreRegex);
if (hrNew->val != NULL)
freeex(hrNew->val);
if (hrNew->header != NULL)
freeex(hrNew->header);
if (hrNew->regex != NULL)
freeex(hrNew->regex);
freeex((headerrule_t *)hrNew);
}
}
catch (ex)
rethrow;
}
/* --mailfrom SINGLE */
try {
char *cp;
if ( (val_get(ctx->val, "option.mailfrom", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--mailfrom = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
ctx->option_mailfrom = strdup(ov->data.s);
/* protect ourselfs from the substitution of backreferences.
* Missing varargs would cause segfaults. Rewrite capturing
* brackets to clustering syntax. Use poor man's s///g
* simulator as current str library doesn't support global
* substitution */
while (str_parse(ctx->option_mailfrom, "s/(.*?)\\((?!\\?:)(.*)/$1(?:$2/", &cp) > 0) {
free(ctx->option_mailfrom);
ctx->option_mailfrom = cp;
}
if (str_parse("<>", ctx->option_mailfrom) == -1) {
log1(ctx, ERROR, "option --mailfrom, illegal regex (%s)", ctx->option_mailfrom);
throw(0,0,0);
}
}
}
catch (ex)
rethrow;
/* --nodename SINGLE */
try {
if ( (val_get(ctx->val, "option.nodename", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--nodename = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
if (strlen(ov->data.s) > sizeof(ctx->uname.nodename)-1) {
log1(ctx, ERROR, "option --nodename, name (%s) too long", ov->data.s);
throw(0,0,0);
}
strcpy(ctx->uname.nodename, ov->data.s);
}
}
catch (ex)
rethrow;
/* --operationmode SINGLE */
try {
char *cp;
if ( (val_get(ctx->val, "option.operationmode", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--operationmode = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
cp = strdup(ov->data.s);
if (strcasecmp(cp, "post") == 0)
ctx->option_operationmode = OPERATIONMODE_POST;
else if (strcasecmp(cp, "feed") == 0)
ctx->option_operationmode = OPERATIONMODE_FEED;
else {
ctx->option_operationmode = OPERATIONMODE_FAKE;
if (strlen(cp) != 9) {
log1(ctx, ERROR, "option --operationmode, invalid length (%s)", cp);
throw(0,0,0);
}
if (cp[3] != '/') {
log1(ctx, ERROR, "option --operationmode, missing slash (%s)", cp);
throw(0,0,0);
}
cp[3] = NUL;
ctx->option_operationmodefakestatus = &cp[0];
ctx->option_operationmodefakedsn = &cp[4];
if ( strlen(ctx->option_operationmodefakestatus) != 3
|| !isdigit((int)ctx->option_operationmodefakestatus[0])
|| !isdigit((int)ctx->option_operationmodefakestatus[1])
|| !isdigit((int)ctx->option_operationmodefakestatus[2])
) {
log1(ctx, ERROR, "option --operationmode, invalid status code (%s)", cp);
throw(0,0,0);
}
if ( (strlen(ctx->option_operationmodefakedsn) != 5)
|| !isdigit((int)ctx->option_operationmodefakedsn[0])
|| (ctx->option_operationmodefakedsn[1] != '.')
|| !isdigit((int)ctx->option_operationmodefakedsn[2])
|| (ctx->option_operationmodefakedsn[3] != '.')
|| !isdigit((int)ctx->option_operationmodefakedsn[4])
|| (ctx->option_operationmodefakedsn[0] != ctx->option_operationmodefakestatus[0])
) {
log1(ctx, ERROR, "option --operationmode, invalid dsn code (%s)", cp);
throw(0,0,0);
}
}
}
else {
log0(ctx, ERROR, "option --operationmode, is mandatory but neither given nor preset (internal)");
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --restrictheader SINGLE */
try {
char *cp;
if ( (val_get(ctx->val, "option.restrictheader", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--restrictheader = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
ctx->option_restrictheader = strdup(ov->data.s);
/* protect ourselfs from the substitution of backreferences.
* Missing varargs would cause segfaults. Rewrite capturing
* brackets to clustering syntax. Use poor man's s///g
* simulator as current str library doesn't support global
* substitution */
while (str_parse(ctx->option_restrictheader, "s/(.*?)\\((?!\\?:)(.*)/$1(?:$2/", &cp) > 0) {
free(ctx->option_restrictheader);
ctx->option_restrictheader = cp;
}
if (str_parse("<>", ctx->option_restrictheader) == -1) {
log1(ctx, ERROR, "option --restrictheader, illegal regex (%s)", ctx->option_restrictheader);
throw(0,0,0);
}
}
}
catch (ex)
rethrow;
/* --size SINGLE */
try {
if ( (val_get(ctx->val, "option.size", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--size = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_maxmessagesize = atoi(ov->data.s)) <= 0) {
log1(ctx, ERROR, "option --size, number (%d) out of range", ctx->option_maxmessagesize);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --timeoutlmtp SINGLE */
try {
int i;
if ( (val_get(ctx->val, "option.timeoutlmtp", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutlmtp= \"%s\"", ov->data.s);
if (ov->ndata == 1) {
if ((i = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutlmtp, number (%d) out of range", i);
throw(0,0,0);
}
ctx->option_timeout_lmtp_accept = i;
ctx->option_timeout_lmtp_read = i;
ctx->option_timeout_lmtp_write = i;
}
}
catch (ex)
rethrow;
/* --timeoutlmtpaccept SINGLE */
try {
if ( (val_get(ctx->val, "option.timeoutlmtpaccept", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutlmtpaccept = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_timeout_lmtp_accept = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutlmtpaccept, number (%d) out of range", ctx->option_timeout_lmtp_accept);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --timeoutlmtpread SINGLE */
try {
if ( (val_get(ctx->val, "option.timeoutlmtpread", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutlmtpread = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_timeout_lmtp_read = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutlmtpread, number (%d) out of range", ctx->option_timeout_lmtp_read);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --timeoutlmtpwrite SINGLE */
try {
if ( (val_get(ctx->val, "option.timeoutlmtpwrite", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutlmtpwrite = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_timeout_lmtp_write = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutlmtpwrite, number (%d) out of range", ctx->option_timeout_lmtp_write);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --timeoutnntp SINGLE */
try {
int i;
if ( (val_get(ctx->val, "option.timeoutnntp", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutnntp= \"%s\"", ov->data.s);
if (ov->ndata == 1) {
if ((i = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutnntp, number (%d) out of range", i);
throw(0,0,0);
}
ctx->option_timeout_nntp_connect = i;
ctx->option_timeout_nntp_read = i;
ctx->option_timeout_nntp_write = i;
}
}
catch (ex)
rethrow;
/* --timeoutnntpconnect SINGLE */
try {
if ( (val_get(ctx->val, "option.timeoutnntpconnect", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutnntpconnect = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_timeout_nntp_connect = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutnntpconnect, number (%d) out of range", ctx->option_timeout_nntp_connect);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --timeoutnntpread SINGLE */
try {
if ( (val_get(ctx->val, "option.timeoutnntpread", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutnntpread = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_timeout_nntp_read = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutnntpread, number (%d) out of range", ctx->option_timeout_nntp_read);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --timeoutnntpwrite SINGLE */
try {
if ( (val_get(ctx->val, "option.timeoutnntpwrite", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--timeoutnntpwrite = \"%s\"", ov->data.s);
if (ov->ndata == 1)
if ((ctx->option_timeout_nntp_write = atoi(ov->data.s)) < 0) {
log1(ctx, ERROR, "option --timeoutnntpwrite, number (%d) out of range", ctx->option_timeout_nntp_write);
throw(0,0,0);
}
}
catch (ex)
rethrow;
/* --user SINGLE */
try {
struct passwd *sPasswd;
if ( (val_get(ctx->val, "option.user", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata == 1 && ov->data.s == NULL)
|| (ov->ndata > 1)
) throw(0,0,0);
log1(ctx, TRACE, "--user = \"%s\"", ov->data.s);
if (ov->ndata == 1) {
if (isdigit((int)ov->data.s[0])) {
if ((sPasswd = getpwuid((uid_t)atoi(ov->data.s))) == NULL) {
log1(ctx, ERROR, "option --user, uid (%s) not found", ov->data.s);
throw(0,0,0);
}
}
else {
if ((sPasswd = getpwnam(ov->data.s)) == NULL) {
log1(ctx, ERROR, "option --user, name (%s) not found", ov->data.s);
throw(0,0,0);
}
}
ctx->option_uid = sPasswd->pw_uid;
}
}
catch (ex)
rethrow;
/* --newsgroup MULTI */
try {
char *cp;
int i;
if ( (val_get(ctx->val, "option.newsgroup", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata >= 1 && ov->data.m == NULL)
) throw(0,0,0);
log1(ctx, DEBUG, "ov->ndata = %d", ov->ndata);
for (i = 0; i < ov->ndata; i++)
log2(ctx, TRACE, "--newsgroup[%d] = \"%s\"", i, (ov->data.m)[i]);
if (ov->ndata >= 1) {
for (i = 0; i < ov->ndata; i++) {
cp = (ov->data.m)[i];
log2(ctx, DEBUG, "cp = (data.m)[%d] = \"%s\"", i, cp);
argz_add(&ctx->azGroupargs, &ctx->asGroupargs, cp);
}
}
}
catch (ex)
rethrow;
/* --testfile MULTI */
try {
char *cp;
int i;
char *cpBuf = NULL;
if ( (val_get(ctx->val, "option.testfile", &ov) != VAL_OK)
|| (ov->ndata < 0)
|| (ov->ndata >= 1 && ov->data.m == NULL)
) throw(0,0,0);
log1(ctx, DEBUG, "ov->ndata = %d", ov->ndata);
for (i = 0; i < ov->ndata; i++)
log2(ctx, TRACE, "--testfile[%d] = \"%s\"", i, (ov->data.m)[i]);
if (ov->ndata >= 1) {
for (i = 0; i < ov->ndata; i++) {
cp = (ov->data.m)[i];
log2(ctx, DEBUG, "cp = (data.m)[%d] = \"%s\"", i, cp);
{
const char *filename = cp;
struct stat sb;
volatile int fd = -1;
//ex_t ex;
try {
if (stat(filename, &sb) == -1) throw(0, 0, "stat");
if ((cpBuf = (char *)malloc((size_t)sb.st_size + 1)) == NULL) throw(0, 0, "malloc");
if ((fd = open(filename, O_RDONLY)) == -1) throw(0, 0, "open");
if (read(fd, (void *)cpBuf, (size_t)sb.st_size) != (ssize_t)sb.st_size) throw(0, 0, "read");
cpBuf[(int)sb.st_size] = '\0';
}
cleanup {
if (fd != -1) close(fd);
}
catch (ex) {
fprintf(stderr, "ERROR: caught %s\n", ex.ex_value == NULL ? "N/A" : (char *)ex.ex_value);
rethrow;
}
}
//printf("DEBUG: *** 1 *** file as it was just read in ***\n%s***\n", cpBuf);
{
volatile msg_t *msg = NULL;
msg_rc_t rc;
try {
if ((msg = msg_create()) == NULL) throw(0, 0, "msg_create");
msg->l2 = ctx->l2;
msg->cpMsg = cpBuf;
if ((rc = msg_split((msg_t *)msg)) != MSG_OK) {
log1(ctx, ERROR, "Error splitting message: %s", msg_error(rc));
throw(0, 0, "msg_split");
}
{//FIXME
char *cp;
cp = NULL;
log0(msg, DEBUG, "FIXME trace #20");
while ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) != NULL) {
log1(msg, DEBUG, "header=\"%s\"", cp);
}
log0(msg, DEBUG, "FIXME trace #21");
}
msg_headermatrixbuildup((msg_t *)msg);
ctx->msg = (msg_t *)msg;
headerrewrite(ctx);
msg_headermatrixteardwn((msg_t *)msg);
{//FIXME
char *cp;
cp = NULL;
log0(msg, DEBUG, "FIXME trace #30");
while ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) != NULL) {
log1(msg, DEBUG, "header=\"%s\"", cp);
}
log0(msg, DEBUG, "FIXME trace #31");
}
argz_add(&((msg_t *)msg)->azNewsgroups, &((msg_t *)msg)->asNewsgroups, "invalid.tst"); //FIXME
if ((rc = msg_join((msg_t *)msg)) != MSG_OK) {
log1(ctx, ERROR, "Error joining message: %s", msg_error(rc));
throw(0, 0, "msg_split");
}
printf("%s", msg->cpMsg); //FIXME
}
cleanup {
if (msg != NULL)
msg_destroy((msg_t *)msg);
ctx->msg = NULL;
}
catch (ex) {
rethrow;
}
}
}
exit(0); //FIXME
}
}
catch (ex)
rethrow;
CUS:
return;
}
static void headerdestroy(headerdata_t *hdC)
{
int i;
if (hdC->ndata > 1) {
for (i = 0; i < hdC->ndata; i++) {
if (hdC->data.m[i] == NULL)
break;
free(hdC->data.m[i]);
}
free (hdC->data.m);
} else
if (hdC->ndata == 1)
if (hdC->data.s != NULL)
free(hdC->data.s);
if (hdC->name != NULL)
free(hdC->name);
if (hdC->prev != NULL && hdC->prev->next == hdC)
throw(0,0,0);
if (hdC->next != NULL && hdC->next->prev == hdC)
throw(0,0,0);
free(hdC);
}
static void headerdelete(headerdata_t *hdC)
{
if (hdC->prev != NULL)
hdC->prev->next = hdC->next;
hdC->next = NULL;
if (hdC->next != NULL)
hdC->next->prev = hdC->prev;
hdC->prev = NULL;
headerdestroy(hdC);
}
static void headerreplace(headerdata_t *hdC, headerdata_t *hdNew)
{
hdNew->prev = hdC->prev;
hdC->prev = NULL;
hdNew->next = hdC->next;
hdC->next = NULL;
if (hdNew->prev != NULL)
hdNew->prev->next = hdNew;
if (hdNew->next != NULL)
hdNew->next->prev = hdNew;
headerdestroy(hdC);
}
static headerdata_t *headercreate(void)
{
ex_t ex;
volatile headerdata_t *hdNew = NULL;
try {
hdNew = mallocex(sizeof(headerdata_t));
hdNew->prev = NULL;
hdNew->next = NULL;
hdNew->name = NULL;
hdNew->ndata = 0;
}
catch (ex) {
if (hdNew != NULL)
free((headerdata_t *)hdNew);
rethrow;
}
return (headerdata_t *)hdNew;
}
struct regex_ctx_st; //FIXME go into a header!
typedef struct regex_ctx_st regex_ctx_t;
struct regex_ctx_st {
int nMatch;
const char **acpMatch;
l2_env_t *l2_env;
l2_channel_t *l2;
};
static var_rc_t regex_lookup(
var_t *var, void *_ctx,
const char *var_ptr, size_t var_len, int var_idx,
const char **val_ptr, size_t *val_len, size_t *val_size)
{
regex_ctx_t *ctx = (regex_ctx_t *)_ctx;
var_rc_t rc;
char *cp;
int i;
log2(ctx, DEBUG, "rgx_lookup variable \"%s\" (%d)", var_ptr, var_len);
rc = VAR_ERR_UNDEFINED_VARIABLE;
i = atoi(var_ptr); /* works with both '}' and '\0' termination */
if (i < ctx->nMatch) {
*val_ptr = ctx->acpMatch[i];
*val_len = strlen(ctx->acpMatch[i]);
*val_size = 0;
rc = VAR_OK;
}
if (rc == VAR_OK)
log4(ctx, DEBUG, "rgx_lookup variable \"%s\" (%d) ok: result is \"%s\" (%d)", var_ptr, var_len, *val_ptr, *val_len);
else
log4(ctx, DEBUG, "rgx_lookup variable \"%s\" (%d) failed: %s (%d)", var_ptr, var_len, var_strerror(var, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
return rc;
}
void msg_headermatrixbuildup(msg_t *msg)
{
ex_t ex;
volatile headerdata_t *hdNew = NULL;
try {
headerdata_t *hdI, *hdP;
char *cp;
cp = NULL;
while ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) != NULL) { /* for each message header */
/*FIXME we want O(1) here */
for (hdP = NULL, hdI = msg->hdFirst; hdI != NULL; hdP = hdI, hdI = hdI->next) { /* for each matrix header */
if (hdI->name == NULL || strlen(hdI->name) == 0 || hdI->ndata == 0)
continue;
if (strcasecmp(cp, hdI->name) == 0)
break;
}
if (hdI == NULL) {
hdNew = headercreate(); /* not found, create new */
hdNew->name = strdupex(cp);
hdI = (headerdata_t *)hdNew; hdNew = NULL; /* ex cleanup */
if (hdP == NULL)
msg->hdFirst = hdI; /* no previous? this is the first */
else {
hdP->next = hdI;
hdI->prev = hdP;
}
}
cp = argz_next(msg->azHeaders, msg->asHeaders, cp);
if (hdI->ndata == 0) {
log1(msg, DEBUG, "header=%s, currently empty", hdI->name);
hdI->data.s = strdupex(cp);
hdI->ndata = 1;
}
else if(hdI->ndata == 1) {
char *cpOld;
cpOld = hdI->data.s;
log1(msg, DEBUG, "header=%s, currently single valued", hdI->name);
hdI->data.m = (char **)mallocex(3 * sizeof(char *));
hdI->data.m[0] = strdupex(cpOld); //FIXME
hdI->data.m[1] = strdupex(cp);
hdI->data.m[2] = NULL;
hdI->ndata = 2;
}
else {
log2(msg, DEBUG, "header=%s, currently multi valued %d", hdI->name, hdI->ndata);
hdI->data.m = (char **)reallocex(hdI->data.m, (hdI->ndata + 2) * sizeof(char *));
hdI->data.m[hdI->ndata++] = strdupex(cp);
hdI->data.m[hdI->ndata] = NULL;
}
}
}
cleanup {
if (hdNew != NULL)
free((headerdata_t *)hdNew);
}
catch(ex) {
rethrow;
}
}
void msg_headermatrixteardwn(msg_t *msg)
{
ex_t ex;
try {
headerdata_t *hdI;
if (msg->azHeaders != NULL)
free(msg->azHeaders);
msg->azHeaders = NULL;
msg->asHeaders = 0;
for (hdI = msg->hdFirst; hdI != NULL; hdI = hdI->next) { /* for each matrix header */
log2(msg, DEBUG, "FIXME trace loop hdI=%.8lx, hI->name=\"%s\"", hdI, hdI->name);
if (hdI->name == NULL || strlen(hdI->name) == 0 || hdI->ndata == 0)
continue;
if (hdI->ndata == 0) {
log1(msg, DEBUG, "header=%s, no data", hdI->name);
}
else if(hdI->ndata == 1) { /* header data is single valued */
log2(msg, DEBUG, "header=%s, data=%s", hdI->name, hdI->data.s);
argz_add(&msg->azHeaders, &msg->asHeaders, hdI->name);
argz_add(&msg->azHeaders, &msg->asHeaders, hdI->data.s);
}
else { /* header data is multi valued */
int i;
for (i = 0; i < hdI->ndata; i++) {
log3(msg, DEBUG, "header=%s[%d], data=%s", hdI->name, i, hdI->data.m[i]);
argz_add(&msg->azHeaders, &msg->asHeaders, hdI->name);
argz_add(&msg->azHeaders, &msg->asHeaders, hdI->data.m[i]);
}
}
}
}
catch(ex) {
rethrow;
}
}
void headerrewrite(lmtp2nntp_t *ctx)
{
headerrule_t *hrI;
headerdata_t *hdI, *hdNew;
regex_ctx_t *regex_ctx;
#define OVECSIZE 30
int ovec[OVECSIZE];
var_rc_t rc; char *cp; //FIXME what a bad name, it's not the returncode of this function
/* short circuit in case no headerrules were set up */
if (ctx->option_firstheaderrule == NULL)
return;
{ //FIXME debug code block
int i;
headerrule_t *hrD;
headerdata_t *hdD;
log0(ctx, DEBUG, "FIXME trace ---------- headerrewrite() ----------");
for (hrD = ctx->option_firstheaderrule; hrD != NULL; hrD = hrD->next)
log1(ctx, DEBUG, "hrD->header=%s", hrD->header);
for (hdD = ctx->msg->hdFirst; hdD != NULL; hdD = hdD->next) {
if (hdD->ndata == 0)
log1(ctx, DEBUG, "hdD->name=%s: (NO DATA)", hdD->name);
if (hdD->ndata == 1)
log2(ctx, DEBUG, "hdD->name:hdD->data.s %s %s", hdD->name, hdD->data.s);
if (hdD->ndata > 1)
for (i = 0; i < hdD->ndata; i++)
log3(ctx, DEBUG, "hdD->name:hdD->data.m[%d] %s %s", i, hdD->name, hdD->data.m[i]);
}
}
regex_ctx = (regex_ctx_t *)mallocex(sizeof(regex_ctx_t));
regex_ctx->nMatch = 0;
regex_ctx->acpMatch = NULL;
regex_ctx->l2_env = ctx->l2_env;
regex_ctx->l2 = ctx->l2;
if ((rc = var_config(ctx->config_varregex, VAR_CONFIG_CB_VALUE, regex_lookup, regex_ctx)) != VAR_OK) {
log2(ctx, ERROR, "configure regex callback failed with %s (%d)", var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
throw(0,0,0);
}
for (hrI = ctx->option_firstheaderrule; hrI != NULL; hrI = hrI->next) { /* for each rule */
{ //FIXME debug code block
int i;
headerrule_t *hrD;
headerdata_t *hdD;
log0(ctx, DEBUG, "FIXME trace ---------- headerrewrite() ---------- MIDDLE");
for (hrD = ctx->option_firstheaderrule; hrD != NULL; hrD = hrD->next)
log1(ctx, DEBUG, "hrD->header=%s", hrD->header);
for (hdD = ctx->msg->hdFirst; hdD != NULL; hdD = hdD->next) {
//log3(ctx, DEBUG, "hdD=%.8lx, hdD->name=%.8lx, hdD->data.s=%.8lx", (long)hdD, (long)&hdD->name, (long)&hdD->data.s);
if (hdD->ndata == 0)
log1(ctx, DEBUG, "hdD->name=%s: (NO DATA)", hdD->name);
if (hdD->ndata == 1)
log2(ctx, DEBUG, "hdD->name:hdD->data.s %s %s", hdD->name, hdD->data.s);
if (hdD->ndata > 1)
for (i = 0; i < hdD->ndata; i++)
log3(ctx, DEBUG, "hdD->name:hdD->data.m[%d] %s %s", i, hdD->name, hdD->data.m[i]);
}
}
if (hrI->regex != NULL) {
log1(ctx, DEBUG, "rule has regex %s", hrI->regex);
for (hdI = ctx->msg->hdFirst; hdI != NULL; hdI = hdI->next) { /* for each header */
if (hdI->name == NULL || strlen(hdI->name) == 0 || hdI->ndata == 0)
continue;
regex_ctx->nMatch = pcre_exec(hrI->pcreRegex, hrI->pcreExtra, hdI->name, strlen(hdI->name), 0, 0, ovec, OVECSIZE);
if (regex_ctx->nMatch >= 1) {
int i;
char *cp;
log1(ctx, DEBUG, "regex matches, %d references", regex_ctx->nMatch);
pcre_get_substring_list(hdI->name, ovec, regex_ctx->nMatch, ®ex_ctx->acpMatch);
if (regex_ctx->acpMatch != NULL)
for (i = 0; i < regex_ctx->nMatch; i++)
log2(ctx, DEBUG, "regex reference[%d]=\'%s\'", i, regex_ctx->acpMatch[i] == NULL ? "(UNDEFINED)" : regex_ctx->acpMatch[i]);
hdNew = headercreate();
/* expanding regex references into header name */
{
var_rc_t var_rc;
char *res_ptr;
log1(ctx, DEBUG, "expanding regex references in headername '%s'", hrI->header);
if ((var_rc = var_expand(ctx->config_varregex, hrI->header, strlen(hrI->header), &res_ptr, NULL, FALSE)) != VAR_OK) {
log3(ctx, ERROR, "expansion of '%s' failed: %s", hrI->header, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
}
log1(ctx, DEBUG, "expansion result '%s'", res_ptr);
if (strlen(res_ptr) == 0) {
log0(ctx, DEBUG, "marking deleted - emtpy headername");
hdNew->name = NULL; //FIXME rename ->header to ->name
/*FIXME clean up data.s and data.m */
hdNew->ndata = 0;
}
else {
hdNew->name = res_ptr; //FIXME rename ->header to ->name
}
}
if (hrI->val == NULL) {
log0(ctx, DEBUG, "marking deleted - empty headervalue before expansion");
/*FIXME clean up data.s and data.m */
hdNew->ndata = 0;
}
else {
/* expanding regex references into header value */
{
var_rc_t var_rc;
char *res_ptr;
log1(ctx, DEBUG, "expanding regex references in header value '%s'", hrI->val);
if ((var_rc = var_expand(ctx->config_varregex, hrI->val, strlen(hrI->val), &res_ptr, NULL, FALSE)) != VAR_OK) {
log3(ctx, ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
}
log1(ctx, DEBUG, "expansion result '%s'", res_ptr);
cp = res_ptr;
}
/* expanding header and other variables into header value */
if (hrI->val != NULL) {
var_rc_t var_rc;
char *res_ptr;
log1(ctx, DEBUG, "expanding variables in header value '%s'", hrI->val);
if ((var_rc = var_expand(ctx->config_varctx, cp, strlen(cp), &res_ptr, NULL, FALSE)) != VAR_OK) {
log3(ctx, ERROR, "expansion of '%s' failed: %s", cp, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
}
log1(ctx, DEBUG, "expansion result '%s'", res_ptr);
if (strlen(res_ptr) == 0) {
log0(ctx, DEBUG, "marking deleted - empty headervalue after expansion");
/*FIXME clean up data.s and data.m */
hdNew->ndata = 0;
}
else {
hdNew->data.s = res_ptr;
hdNew->ndata = 1;
}
}
}
/*FIXME clean up data.m */
headerreplace(hdI, hdNew);
if (hdNew->prev == NULL)
ctx->msg->hdFirst = hdNew;
hdI = hdNew;
}
}
}
else {
log1(ctx, DEBUG, "rule has no regex but static header %s", hrI->header);
hdNew = headercreate();
hdNew->name = strdupex(hrI->header); //FIXME rename ->header to ->name
if (hrI->val == NULL) {
log0(ctx, DEBUG, "marking deleted");
/*FIXME clean up data.s and data.m */
hdNew->ndata = 0;
}
else {
/*FIXME clean up data.m */
/* expanding header and other variables into header value */
var_rc_t var_rc;
char *res_ptr;
log1(ctx, DEBUG, "expanding variables in header value '%s'", hrI->val);
if ((var_rc = var_expand(ctx->config_varctx, hrI->val, strlen(hrI->val), &res_ptr, NULL, FALSE)) != VAR_OK) {
log3(ctx, ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc);
}
log1(ctx, DEBUG, "expansion result '%s'", res_ptr);
if (strlen(res_ptr) == 0) {
log0(ctx, DEBUG, "marking deleted - empty headervalue after expansion");
/*FIXME clean up data.s and data.m */
hdNew->ndata = 0;
}
else {
hdNew->data.s = res_ptr;
hdNew->ndata = 1;
}
}
for (hdI = ctx->msg->hdFirst; hdI != NULL; hdI = hdI->next) { /* for each header */
if (hdI->name == NULL || strlen(hdI->name) == 0)
continue;
log2(ctx, DEBUG, "hrI->header=%s, hdI->name=%s", hrI->header, hdI->name);
if (strcasecmp(hrI->header, hdI->name) == 0)
break;
}
if (hdI != NULL) {
log1(ctx, DEBUG, "replacing header %s", hrI->header);
headerreplace(hdI, hdNew);
if (hdNew->prev == NULL) {
log0(ctx, DEBUG, "FIXME trace #1");
ctx->msg->hdFirst = hdNew;
}
}
else {
log1(ctx, DEBUG, "appending header %s", hrI->header);
for (hdI = ctx->msg->hdFirst; hdI->next != NULL; hdI = hdI->next);
hdI->next = hdNew;
hdNew->prev = hdI;
}
}
}
{ //FIXME debug code block
int i;
headerrule_t *hrD;
headerdata_t *hdD;
log0(ctx, DEBUG, "FIXME trace ---------- headerrewrite() ---------- FINISH");
for (hrD = ctx->option_firstheaderrule; hrD != NULL; hrD = hrD->next)
log1(ctx, DEBUG, "hrD->header=%s", hrD->header);
for (hdD = ctx->msg->hdFirst; hdD != NULL; hdD = hdD->next) {
if (hdD->ndata == 0)
log1(ctx, DEBUG, "hdD->name=%s: (NO DATA)", hdD->name);
if (hdD->ndata == 1)
log2(ctx, DEBUG, "hdD->name:hdD->data.s %s %s", hdD->name, hdD->data.s);
if (hdD->ndata > 1)
for (i = 0; i < hdD->ndata; i++)
log3(ctx, DEBUG, "hdD->name:hdD->data.m[%d] %s %s", i, hdD->name, hdD->data.m[i]);
}
}
}