/* ** 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]); } } }