head 1.43; access; symbols LMTP2NNTP_1_4_1:1.42 LMTP2NNTP_1_4_0:1.41 LMTP2NNTP_1_3_0:1.41 LMTP2NNTP_1_3b2:1.41 LMTP2NNTP_1_3b1:1.41 LMTP2NNTP_1_3a3:1.41 LMTP2NNTP_1_3a2:1.41 LMTP2NNTP_1_3a1:1.41 LMTP2NNTP_1_2_0:1.40 LMTP2NNTP_1_2b4:1.40 LMTP2NNTP_1_2b3:1.39 LMTP2NNTP_1_2b2:1.38 LMTP2NNTP_1_2b1:1.36 LMTP2NNTP_1_2a8:1.36 LMTP2NNTP_1_2a7:1.35 LMTP2NNTP_1_2a6:1.29 LMTP2NNTP_1_2a5:1.28 LMTP2NNTP_1_2a4:1.4 LMTP2NNTP_1_2a3:1.3 SOURCE_RESTRUCTURING_AFTER:1.1; locks; strict; comment @ * @; 1.43 date 2005.10.22.10.33.19; author rse; state Exp; branches; next 1.42; 1.42 date 2005.10.09.21.28.07; author thl; state Exp; branches; next 1.41; 1.41 date 2004.04.02.10.38.13; author thl; state Exp; branches; next 1.40; 1.40 date 2003.02.12.15.24.18; author thl; state Exp; branches; next 1.39; 1.39 date 2003.02.12.12.27.01; author rse; state Exp; branches; next 1.38; 1.38 date 2003.02.11.15.03.18; author thl; state Exp; branches; next 1.37; 1.37 date 2003.02.10.10.13.12; author thl; state Exp; branches; next 1.36; 1.36 date 2003.02.06.13.50.08; author thl; state Exp; branches; next 1.35; 1.35 date 2003.02.04.14.50.45; author thl; state Exp; branches; next 1.34; 1.34 date 2003.01.30.19.46.42; author rse; state Exp; branches; next 1.33; 1.33 date 2003.01.30.19.45.20; author rse; state Exp; branches; next 1.32; 1.32 date 2003.01.30.19.42.13; author rse; state Exp; branches; next 1.31; 1.31 date 2003.01.30.19.27.29; author rse; state Exp; branches; next 1.30; 1.30 date 2003.01.30.13.30.41; author thl; state Exp; branches; next 1.29; 1.29 date 2002.07.02.07.49.01; author rse; state Exp; branches; next 1.28; 1.28 date 2002.05.29.21.35.52; author thl; state Exp; branches; next 1.27; 1.27 date 2002.05.29.10.21.33; author thl; state Exp; branches; next 1.26; 1.26 date 2002.05.28.14.17.23; author thl; state Exp; branches; next 1.25; 1.25 date 2002.05.28.14.14.01; author thl; state Exp; branches; next 1.24; 1.24 date 2002.05.28.13.54.22; author thl; state Exp; branches; next 1.23; 1.23 date 2002.05.28.12.43.14; author thl; state Exp; branches; next 1.22; 1.22 date 2002.05.28.12.32.58; author thl; state Exp; branches; next 1.21; 1.21 date 2002.05.28.12.30.29; author thl; state Exp; branches; next 1.20; 1.20 date 2002.05.28.12.21.38; author thl; state Exp; branches; next 1.19; 1.19 date 2002.05.28.12.19.47; author thl; state Exp; branches; next 1.18; 1.18 date 2002.05.28.11.29.00; author thl; state Exp; branches; next 1.17; 1.17 date 2002.05.28.07.53.16; author thl; state Exp; branches; next 1.16; 1.16 date 2002.05.27.15.37.21; author thl; state Exp; branches; next 1.15; 1.15 date 2002.05.27.15.09.50; author thl; state Exp; branches; next 1.14; 1.14 date 2002.05.23.09.37.15; author thl; state Exp; branches; next 1.13; 1.13 date 2002.05.23.09.34.27; author thl; state Exp; branches; next 1.12; 1.12 date 2002.05.23.09.13.20; author thl; state Exp; branches; next 1.11; 1.11 date 2002.05.23.08.35.12; author thl; state Exp; branches; next 1.10; 1.10 date 2002.04.25.09.14.28; author thl; state Exp; branches; next 1.9; 1.9 date 2002.04.24.09.26.03; author thl; state Exp; branches; next 1.8; 1.8 date 2002.04.23.14.26.32; author thl; state Exp; branches; next 1.7; 1.7 date 2002.04.18.15.38.59; author thl; state Exp; branches; next 1.6; 1.6 date 2002.04.18.15.27.40; author thl; state Exp; branches; next 1.5; 1.5 date 2002.04.18.14.09.25; author thl; state Exp; branches; next 1.4; 1.4 date 2002.03.13.14.41.13; author thl; state Exp; branches; next 1.3; 1.3 date 2002.02.26.15.56.08; author thl; state Exp; branches; next 1.2; 1.2 date 2001.12.31.15.15.36; author thl; state Exp; branches; next 1.1; 1.1 date 2001.12.31.11.09.53; author thl; state Exp; branches; next ; desc @@ 1.43 log @we require for time(3) @ text @/* ** OSSP lmtp2nntp - Mail to News Gateway ** Copyright (c) 2001-2003 Ralf S. Engelschall ** Copyright (c) 2001-2003 The OSSP Project ** Copyright (c) 2001-2003 Cable & Wireless Germany ** ** 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/tool/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_msg.c: mail message manipulation library */ #include #include #include #include "lmtp2nntp_msg.h" #include "lmtp2nntp_argz.h" #include "lmtp2nntp_common.h" /* only required for logbook() */ #include "str.h" /* third party */ #include "l2.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(HAVE_DMALLOC_H) && defined(WITH_DMALLOC) #include "dmalloc.h" #endif msg_t *msg_create(val_t *prival) { ex_t ex; msg_t *msg; if (prival == NULL) return NULL; if ((msg = (msg_t *)malloc(sizeof(msg_t))) == NULL) return NULL; msg->azEnvgroups = NULL; msg->asEnvgroups = 0; msg->cpMsg = NULL; msg->azHeaders = NULL; msg->asHeaders = 0; msg->hdFirst = NULL; msg->cpFid = NULL; msg->cpBody = NULL; msg->cpMsgid = NULL; msg->mail_from = NULL; msg->azRcpt = NULL; msg->asRcpt = 0; msg->azNewsgroups = NULL; msg->asNewsgroups = 0; msg->l2 = NULL; /* this is a copy only */ msg->prival = prival; /* create printable variables context and mount it into the private application context */ try { val_reg(msg->prival, "msg", VAL_TYPE_PTR, "msg context structure", NULL); val_set(msg->prival, "msg", msg); } catch (ex) rethrow; return msg; } void msg_destroy(msg_t *msg) { if (msg == NULL) return; if (msg->azEnvgroups != NULL) free(msg->azEnvgroups); if (msg->cpMsg != NULL) free(msg->cpMsg); if (msg->azHeaders != NULL) free(msg->azHeaders); if (msg->cpFid != NULL) free(msg->cpFid); if (msg->cpBody != NULL) free(msg->cpBody); if (msg->cpMsgid != NULL) free(msg->cpMsgid); if (msg->mail_from != NULL) free(msg->mail_from); if (msg->azRcpt != NULL) free(msg->azRcpt); if (msg->azNewsgroups != NULL) free(msg->azNewsgroups); msg->l2 = NULL; /* this is a copy only, the "parent" needs to clean this up */ if (msg->prival != NULL) val_unreg(msg->prival, "msg"); free(msg); return; } msg_rc_t msg_split(msg_t *msg) { char *cpName; char *cpValue; char *cpRem; /* Remainder */ char *cp; char *cpHeaders; /* INPUTS * * msg->cpMsg * must contain the wholly RFC0822 formatted message with native * (unescaped) dots at the beginning of a line, the 'From ' envelope, * headers, double newline, body, NUL, no trailing dot; * * OUTPUTS * * msg->cpMsg * free()d and set to NULL * * msg->azHeaders, msg->asHeaders contains the headers in argz format, one * logical NUL-terminated line per header which might be wrapped into * multiple '\n'-ended physical lines. The "From " envelope, "Received:", * "Path:", "To:" and "Cc:" headers are removed silently. The * "Newsgroups:" and "Message-ID" headers are removed and their values are * stored in separate structures (see below). * * msg->cpBody * contains the unmodified body of the message, NUL-terminated, no * trailing dot. * * msg->cpMsgid * contains the message id including surrounding angle brackets. * * msg->azNewsgroups, asNewsgroups * is a argz-type array of strings containing the Newsgroups based on the * header information. */ logbook(msg->l2, L2_LEVEL_DEBUG, "split message into header and body"); if (str_parse(msg->cpMsg, "m/((?:.*?)\\n)\\n(.*)$/s", &cpHeaders, &msg->cpBody) <= 0) return MSG_ERR_SPLITHEADBODY; free(msg->cpMsg); msg->cpMsg = NULL; logbook(msg->l2, L2_LEVEL_DEBUG, "replace envelope-from by -FE: pseudoheader"); /* This eliminates the special case of having one header, which is really * an embedded envelope, not ending with a colon while all others do. */ if (strncasecmp(cpHeaders, "From", 4) == 0) memcpy(cpHeaders, "-EF:", 4); logbook(msg->l2, L2_LEVEL_DEBUG, "unwrap header lines"); /* poor man's s///g simulator as current str library doesn't support global substitution */ while (str_parse(cpHeaders, "s/(.*?)\\n[ \\t]+(.*)/$1 $2/s", &cpRem) > 0) { free(cpHeaders); cpHeaders = cpRem; } logbook(msg->l2, L2_LEVEL_DEBUG, "split header lines into names and values"); while (str_parse(cpHeaders, "m/^[> \\t]*([\\x21-\\x7e]+?:)[ \\t]*([^\\n]*?)[ \\t]*\\n(.*)/s", &cpName, &cpValue, &cpRem) > 0) { free(cpHeaders); cpHeaders = cpRem; argz_add(&msg->azHeaders, &msg->asHeaders, cpName); argz_add(&msg->azHeaders, &msg->asHeaders, cpValue); free(cpName); free(cpValue); } free(cpHeaders); logbook(msg->l2, L2_LEVEL_DEBUG, "check for headers we care about and do whatever neccessary"); msg->azNewsgroups = NULL; msg->asNewsgroups = 0; cp = msg->azHeaders; while (cp != NULL) { logbook(msg->l2, L2_LEVEL_DEBUG, "processing header \"%s\" in split", cp); if (strcasecmp("Received:", cp) == 0) { argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ if ((msg->cpFid == NULL) && (str_parse(cp, "m/\\sid\\s+cpFid) > 0)) logbook(msg->l2, L2_LEVEL_DEBUG, "found foreign-ID \"%s\" for logging", msg->cpFid); argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ continue; } if (strcasecmp("Newsgroups:", cp) == 0) { if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next value */ break; if (argz_add(&msg->azNewsgroups, &msg->asNewsgroups, cp) != 0) /* get value */ return MSG_ERR_MEM; if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next value */ break; continue; } if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next value */ break; if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next name */ break; } logbook(msg->l2, L2_LEVEL_DEBUG, "checking Newsgroups"); if (msg->azNewsgroups != NULL) { argz_stringify(msg->azNewsgroups, msg->asNewsgroups, ','); if (argz_create_sep(msg->azNewsgroups, ',', &msg->azNewsgroups, &msg->asNewsgroups) != 0) return MSG_ERR_MEM; } logbook(msg->l2, L2_LEVEL_DEBUG, "split complete"); return MSG_OK; } msg_rc_t msg_join(msg_t *msg) { char *cp; char *cpRem; char **aHeaders; int i; int o; char *cpCut; char *cpWrap; char c; char cOld; int n; char *cpHeaders; char *azNewheaders; size_t asNewheaders; logbook(msg->l2, L2_LEVEL_DEBUG, "verify Newsgroups"); if ((msg->azNewsgroups == NULL) || (msg->asNewsgroups == 0)) return MSG_ERR_JOINGROUPNONE; argz_stringify(msg->azNewsgroups, msg->asNewsgroups, ','); if (strlen(msg->azNewsgroups) == 0) return MSG_ERR_JOINGROUPEMPTY; argz_add(&msg->azHeaders, &msg->asHeaders, "Newsgroups:"); argz_add(&msg->azHeaders, &msg->asHeaders, msg->azNewsgroups); logbook(msg->l2, L2_LEVEL_DEBUG, "verify Message-ID"); cp = msg->azHeaders; while (cp != NULL) { logbook(msg->l2, L2_LEVEL_DEBUG, "processing header \"%s\" in join", cp); if (strcasecmp("Message-ID:", cp) == 0) { if (msg->cpMsgid != NULL) return MSG_ERR_JOINIDMULTI; if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next value */ break; if ((cp == NULL) || (strlen(cp) == 0)) /* get value */ return MSG_ERR_JOINIDEMPTY; if ((msg->cpMsgid = strdup(cp)) == NULL) return MSG_ERR_MEM; if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next value */ break; continue; } if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next value */ break; if ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) == NULL) /* next name */ break; } if (msg->cpMsgid == NULL) return MSG_ERR_JOINIDNONE; if (strlen(msg->cpMsgid) == 0) return MSG_ERR_JOINIDEMPTY; logbook(msg->l2, L2_LEVEL_DEBUG, "merge name/value pairs into single string"); argz_add(&msg->azHeaders, &msg->asHeaders, ""); /* append empty string */ if ((aHeaders = (char **)malloc((argz_count(msg->azHeaders, msg->asHeaders) + 1) * sizeof(char *))) == NULL) return MSG_ERR_MEM; argz_extract(msg->azHeaders, msg->asHeaders, aHeaders); /* replace the trailing NUL, which is *(cp-1) of the predecessor, with a * space at every second string. Break action when terminating NULL string * is detected */ i=0; while(1) { if ((cp = aHeaders[++i]) == NULL) break; *(cp-1) = ' '; if ((cp = aHeaders[++i]) == NULL) break; } free(aHeaders); logbook(msg->l2, L2_LEVEL_DEBUG, "fold headers"); /* A logical line is split into one or more physical '\n'-terminated * lines. The physical line is never longer than WRAPAT characters. This * includes the folded data and the header name + colon + space for the * first line and WRAPUSING string prefix for all other lines. Leading and * trailing blanks of folded lines are removed while blanks inside the * line are preserved. The header is never left alone in a physical line. * Fragments exceeding WRAPAT characters without having a blank as a * splitting point are forcibly cut at a non-blank character. */ azNewheaders = NULL; asNewheaders = 0; cp = NULL; while ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) != NULL) { if (strlen(cp) > WRAPAT) { cpRem = cp; cpWrap = NULL; for (o = 0; (cpRem[o] != ':') && (cpRem[o] != NUL); o++); /* offset name so at least one char of value remains in first line */ o += 2; /* skip ": " */ while ((strlen(cpRem) + (cpWrap == NULL ? 0 : strlen(WRAPUSING))) > WRAPAT) { for (i = WRAPAT - 1 - (cpWrap == NULL ? 0 : strlen(WRAPUSING)); (i >= o) && !isspace((int)cpRem[i]); i--); if (i < o) i = WRAPAT - 1 - (cpWrap == NULL ? 0 : strlen(WRAPUSING) - 1); /* sorry, forced cut at non-blank */ cpCut = cpRem; cpRem += i; for (; (isspace((int)*cpRem) && (*cpRem != NUL)); cpRem++); /* skip next lines leading blanks */ for (; (i >= o) && isspace((int)cpCut[i-1]); i--); /* chop off this lines trailing blanks */ if (i >= o) { /* only keep line fragment if some non-blanks inside */ if (cpWrap == NULL) { if ((cpWrap = (char *)malloc(i+strlen(WRAPUSING)+1)) == NULL) return MSG_ERR_MEM; *cpWrap = NUL; o = 1; } else { if ((cpWrap = (char *)realloc(cpWrap, strlen(cpWrap)+i+strlen(WRAPUSING)+1)) == NULL) return MSG_ERR_MEM; strcat(cpWrap, WRAPUSING); } strncat(cpWrap, cpCut, i); } } if (strlen(cpRem) > 0) { if ((cpWrap = (char *)realloc(cpWrap, strlen(cpWrap)+strlen(cpRem)+strlen(WRAPUSING)+1)) == NULL) return MSG_ERR_MEM; strcat(cpWrap, WRAPUSING); strcat(cpWrap, cpRem); } argz_add(&azNewheaders, &asNewheaders, cpWrap); logbook(msg->l2, L2_LEVEL_DEBUG, "a folded header \"%{text}D\"", cpWrap, strlen(cpWrap)); free(cpWrap); } else { argz_add(&azNewheaders, &asNewheaders, cp); logbook(msg->l2, L2_LEVEL_DEBUG, "verbatim header \"%{text}D\"", cp, strlen(cp)); } } free(msg->azHeaders); msg->azHeaders = azNewheaders; msg->asHeaders = asNewheaders; logbook(msg->l2, L2_LEVEL_DEBUG, "strigify headers"); argz_stringify(msg->azHeaders, msg->asHeaders, '\n'); cpHeaders = msg->azHeaders; /******************************************************************** * header + CRLF + body + '.' + CRLF + NUL, replacing NL with CRLF * ********************************************************************/ logbook(msg->l2, L2_LEVEL_DEBUG, "assemble header and body"); n = 0; /* count size of headers, reserve space for NL to CRLF conversion */ for (i = 0; ((c = cpHeaders[i]) != NUL); i++) { if (c == '\n') n++; n++; } /* if headers don't end with NL, reserve space for CRLF */ if (i >= 0 && cpHeaders[i - 1] != '\n') n+=2; /* reserve space for CRLF between headers and body */ n+=2; /* count size of body, reserve space for NL-DOT escape and NL to CRLF conversion */ cOld = '\n'; for (i = 0; ((c = msg->cpBody[i]) != NUL); i++) { if (c == '\n') n++; if (c == '.' && cOld == '\n') n++; n++; cOld = c; } /* if body doesn't end with NL, reserve space for CRLF */ if (i >= 0 && msg->cpBody[i - 1] != '\n') n+=2; /* reserve space for terminating '.'-CRLF-NUL at the end of the message */ n+=4; if ((msg->cpMsg = (char *)malloc(n)) == NULL) return MSG_ERR_MEM; n = 0; /* copy headers, do NL to CRLF conversion */ for (i = 0; ((c = cpHeaders[i]) != NUL); i++) { if (c == '\n') msg->cpMsg[n++] = '\r'; msg->cpMsg[n++] = c; } /* if headers don't end with NL, append CRLF */ if (i >= 0 && cpHeaders[i - 1] != '\n') { msg->cpMsg[n++] = '\r'; msg->cpMsg[n++] = '\n'; } /* add CRLF between headers and body */ msg->cpMsg[n++] = '\r'; msg->cpMsg[n++] = '\n'; /* copy body, do NL-DOT escape and NL to CRLF conversion */ cOld = '\n'; for (i = 0; ((c = msg->cpBody[i]) != NUL); i++) { if (c == '\n') msg->cpMsg[n++] = '\r'; if (c == '.' && cOld == '\n') msg->cpMsg[n++] = '.'; msg->cpMsg[n++] = c; cOld = c; } /* if body doesn't end with NL, append CRLF */ if (i >= 0 && msg->cpBody[i - 1] != '\n') { msg->cpMsg[n++] = '\r'; msg->cpMsg[n++] = '\n'; } /* add terminating '.'-CRLF-NUL at the end of the message */ msg->cpMsg[n++] = '.'; msg->cpMsg[n++] = '\r'; msg->cpMsg[n++] = '\n'; msg->cpMsg[n] = NUL; logbook(msg->l2, L2_LEVEL_DEBUG, "join complete"); return MSG_OK; } char *msg_error(msg_rc_t rc) { char *str; str = "MSG: no description"; if (rc == MSG_OK ) str = "MSG: no error"; else if (rc == MSG_ERR_MEM ) str = "MSG: memory"; else if (rc == MSG_ERR_SPLITHEADBODY ) str = "MSG: split into header and body failed"; else if (rc == MSG_ERR_SPLITLEN ) str = "MSG: header is too short"; else if (rc == MSG_ERR_SPLITMISSINGFROM ) str = "MSG: header is missing 'From ' envelope"; else if (rc == MSG_ERR_JOINGROUPNONE ) str = "MSG: join with no 'Newsgroup'"; else if (rc == MSG_ERR_JOINGROUPEMPTY ) str = "MSG: join with empty 'Newsgroup'"; else if (rc == MSG_ERR_JOINIDNONE ) str = "MSG: join with no 'Message-ID'"; else if (rc == MSG_ERR_JOINIDEMPTY ) str = "MSG: join with empty 'Message-ID'"; else if (rc == MSG_ERR_JOINIDMULTI ) str = "MSG: join with multiple 'Message-ID's"; return str; } static void headerdatadestroy(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); hdC->ndata = 0; } else if (hdC->ndata == 1) { if (hdC->data.s != NULL) free(hdC->data.s); hdC->ndata = 0; } } static void headernamedestroy(headerdata_t *hdC) { if (hdC->name != NULL) { free(hdC->name); hdC->name = NULL; } } static void headerdestroy(headerdata_t *hdC) { headerdatadestroy(hdC); headernamedestroy(hdC); 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); } #if 0 /* still unused */ 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); } #endif 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; } 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; logbook(ctx->l2, L2_LEVEL_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) logbook(ctx->l2, L2_LEVEL_DEBUG, "rgx_lookup variable \"%s\" (%d) ok: result is \"%s\" (%d)", var_ptr, var_len, *val_ptr, *val_len); else logbook(ctx->l2, L2_LEVEL_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; } static var_rc_t createmessageid( val_t *prival, const char *cpArg, size_t nArg, const char *cpVal, size_t nVal, char **cppOut, size_t *pnOut, size_t *pnOutsize) { char *cp; int msgcount; msg_t *msg; char *foreignid; char *nodename; time_t cachetime; pid_t cachepid; int size; int maxsize; char *format; if (nVal > 2 && cpVal[0] == '<' && cpVal[nVal-1] == '>') { *cppOut = strdupex(cpVal); *pnOutsize = nVal; *pnOut = nVal; return VAR_OK; } if (val_get(prival, "msgcount", &msgcount) != VAL_OK) msgcount = 0; if (val_get(prival, "msg", &msg) != VAL_OK) msg = NULL; if (msg != NULL && msg->cpFid != NULL) foreignid = msg->cpFid; else foreignid = "noFid"; if (val_get(prival, "nodename", &nodename) != VAL_OK) nodename = "localhost.invalid"; cachetime = time(NULL); cachepid = getpid(); /* idea and first implementation as a contribution to lmtp2nntp v1.0.0 by Christos Ricudis */ maxsize = WRAPAT - strlen("Message-ID: "); format = "<%0.11d$%0.6d$%0.4d$%.20s@@%s>"; size = str_format(NULL, -1, format, cachetime, cachepid, msgcount, foreignid, nodename); if (size >= 38) { if (size > maxsize) size = maxsize; cp = (char *)mallocex(size + 1); size = str_format(cp, size + 1, format, cachetime, cachepid, msgcount, foreignid, nodename); if (size > maxsize) cp[size-2] = '>'; } else cp = strdupex("<>"); /* if format fails, do not end the wholly program abnormally */ *cppOut = strdupex(cp); *pnOutsize = strlen(cp) + 1; *pnOut = strlen(cp); return VAR_OK; } static var_rc_t operate_cb( var_t *var, void *_prival, const char *op_ptr, size_t op_len, const char *arg_ptr, size_t arg_len, const char *val_ptr, size_t val_len, char **out_ptr, size_t *out_len, size_t *out_size) { val_t *prival = _prival; if (op_len == 15 && strncmp(op_ptr, "createmessageid", 15) == 0) { return createmessageid(prival, arg_ptr, arg_len, val_ptr, val_len, out_ptr, out_len, out_size); } else return VAR_ERR_UNDEFINED_OPERATION; } void msg_headermatrixbuildup(msg_t *msg) { ex_t ex; volatile headerdata_t *hdNew = NULL; try { headerdata_t *hdI, *hdP; char *cp; logbook(msg->l2, L2_LEVEL_TRACE, "headermatrix build up"); cp = NULL; while ((cp = argz_next(msg->azHeaders, msg->asHeaders, cp)) != NULL) { /* for each message header */ logbook(msg->l2, L2_LEVEL_DEBUG, "processing message header \"%s\"", cp); /*TODO 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) { logbook(msg->l2, L2_LEVEL_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; logbook(msg->l2, L2_LEVEL_DEBUG, "header=%s, currently single valued", hdI->name); hdI->data.m = (char **)mallocex(3 * sizeof(char *)); hdI->data.m[0] = cpOld; hdI->data.m[1] = strdupex(cp); hdI->data.m[2] = NULL; hdI->ndata = 2; } else { logbook(msg->l2, L2_LEVEL_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; logbook(msg->l2, L2_LEVEL_TRACE, "headermatrix tear down"); try { headerdata_t *hdI; headerdata_t *hdN; if (msg->azHeaders != NULL) free(msg->azHeaders); msg->azHeaders = NULL; msg->asHeaders = 0; hdI = msg->hdFirst; /* for each header */ while (hdI != NULL) { logbook(msg->l2, L2_LEVEL_DEBUG, "processing matrix header \"%s\"", hdI->name); if (hdI->name != NULL && strlen(hdI->name) != 0 && hdI->ndata != 0) { if (hdI->ndata == 0) { logbook(msg->l2, L2_LEVEL_DEBUG, "header=%s, no data", hdI->name); } else if(hdI->ndata == 1) { /* header data is single valued */ logbook(msg->l2, L2_LEVEL_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++) { logbook(msg->l2, L2_LEVEL_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]); } } } /* cleanup */ hdN = hdI->next; hdI->prev = NULL; hdI->next = NULL; headerdestroy(hdI); hdI = hdN; } } 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 var_rc; char *cp; /* short circuit in case no headerrules were set up */ if (ctx->option_firstheaderrule == NULL) return; #if 0 { /* TODO debug only - wait for l2 to support code block bypassing */ int i; headerrule_t *hrD; headerdata_t *hdD; logbook(ctx->l2, L2_LEVEL_DEBUG, "headerrewrite input"); for (hrD = ctx->option_firstheaderrule; hrD != NULL; hrD = hrD->next) logbook(ctx->l2, L2_LEVEL_DEBUG, "hrD->header=%s", hrD->header); for (hdD = ctx->msg->hdFirst; hdD != NULL; hdD = hdD->next) { if (hdD->ndata == 0) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name=%s: (NO DATA)", hdD->name); if (hdD->ndata == 1) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name:hdD->data.s %s %s", hdD->name, hdD->data.s); if (hdD->ndata > 1) for (i = 0; i < hdD->ndata; i++) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name:hdD->data.m[%d] %s %s", i, hdD->name, hdD->data.m[i]); } } #endif 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 ((var_rc = var_config(ctx->config_varregex, VAR_CONFIG_CB_VALUE, regex_lookup, regex_ctx)) != VAR_OK) { logbook(ctx->l2, L2_LEVEL_ERROR, "configure regex callback failed with %s (%d)", var_strerror(ctx->config_varregex, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); throw(0,0,0); } if ((var_rc = var_config(ctx->config_varctx, VAR_CONFIG_CB_OPERATION, operate_cb, ctx->prival)) != VAR_OK) { logbook(ctx->l2, L2_LEVEL_ERROR, "configure operate callback failed with %s (%d)", var_strerror(ctx->config_varctx, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); throw(0,0,0); } for (hrI = ctx->option_firstheaderrule; hrI != NULL; hrI = hrI->next) { /* for each rule */ #if 0 { /* TODO debug only - wait for l2 to support code block bypassing */ int i; headerrule_t *hrD; headerdata_t *hdD; logbook(ctx->l2, L2_LEVEL_DEBUG, "headerrewrite loop for each rule"); for (hrD = ctx->option_firstheaderrule; hrD != NULL; hrD = hrD->next) logbook(ctx->l2, L2_LEVEL_DEBUG, "hrD->header=%s", hrD->header); for (hdD = ctx->msg->hdFirst; hdD != NULL; hdD = hdD->next) { /* logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD=%.8lx, hdD->name=%.8lx, hdD->data.s=%.8lx", (long)hdD, (long)&hdD->name, (long)&hdD->data.s); */ if (hdD->ndata == 0) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name=%s: (NO DATA)", hdD->name); if (hdD->ndata == 1) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name:hdD->data.s %s %s", hdD->name, hdD->data.s); if (hdD->ndata > 1) for (i = 0; i < hdD->ndata; i++) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name:hdD->data.m[%d] %s %s", i, hdD->name, hdD->data.m[i]); } } #endif if (hrI->regex != NULL) { logbook(ctx->l2, L2_LEVEL_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; logbook(ctx->l2, L2_LEVEL_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++) logbook(ctx->l2, L2_LEVEL_DEBUG, "regex reference[%d]=\'%s\'", i, regex_ctx->acpMatch[i] == NULL ? "(UNDEFINED)" : regex_ctx->acpMatch[i]); hdNew = headercreate(); /* expanding regex references into header name */ { char *res_ptr; logbook(ctx->l2, L2_LEVEL_DEBUG, "expanding regex references in headername '%s'", hrI->name); if ((var_rc = var_expand(ctx->config_varregex, hrI->name, strlen(hrI->name), &res_ptr, NULL, FALSE)) != VAR_OK) { logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->name, var_strerror(ctx->config_varregex, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); } logbook(ctx->l2, L2_LEVEL_DEBUG, "expansion result '%s'", res_ptr); if (strlen(res_ptr) == 0) { logbook(ctx->l2, L2_LEVEL_DEBUG, "marking deleted - emtpy headername"); headerdatadestroy(hdNew); headernamedestroy(hdNew); } else { hdNew->name = res_ptr; } } if (hrI->val == NULL) { logbook(ctx->l2, L2_LEVEL_DEBUG, "marking deleted - empty headervalue before expansion"); headerdatadestroy(hdNew); headernamedestroy(hdNew); } else { /* expanding regex references into header value */ { char *res_ptr; logbook(ctx->l2, L2_LEVEL_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) { logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varregex, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); } logbook(ctx->l2, L2_LEVEL_DEBUG, "expansion result '%s'", res_ptr); cp = res_ptr; } /* expanding variables into header value */ if (hrI->val != NULL) { char *res_ptr; logbook(ctx->l2, L2_LEVEL_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) { logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", cp, var_strerror(ctx->config_varctx, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); } logbook(ctx->l2, L2_LEVEL_DEBUG, "expansion result '%s'", res_ptr); if (strlen(res_ptr) == 0) { logbook(ctx->l2, L2_LEVEL_DEBUG, "marking deleted - empty headervalue after expansion"); headerdatadestroy(hdNew); headernamedestroy(hdNew); } else { hdNew->data.s = res_ptr; hdNew->ndata = 1; } } } headerreplace(hdI, hdNew); if (hdNew->prev == NULL) ctx->msg->hdFirst = hdNew; hdI = hdNew; } } } else { logbook(ctx->l2, L2_LEVEL_DEBUG, "rule has no regex but static header %s", hrI->name); hdNew = headercreate(); hdNew->name = strdupex(hrI->name); if (hrI->val == NULL) { logbook(ctx->l2, L2_LEVEL_DEBUG, "marking deleted"); headerdatadestroy(hdNew); headernamedestroy(hdNew); } else { /* expanding variables into header value */ char *res_ptr; logbook(ctx->l2, L2_LEVEL_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) { logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varctx, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); } logbook(ctx->l2, L2_LEVEL_DEBUG, "expansion result '%s'", res_ptr); if (strlen(res_ptr) == 0) { logbook(ctx->l2, L2_LEVEL_DEBUG, "marking deleted - empty headervalue after expansion"); headerdatadestroy(hdNew); headernamedestroy(hdNew); } else { headerdatadestroy(hdNew); 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; logbook(ctx->l2, L2_LEVEL_DEBUG, "hrI->name=%s, hdI->name=%s", hrI->name, hdI->name); if (strcasecmp(hrI->name, hdI->name) == 0) break; } if (hdI != NULL) { logbook(ctx->l2, L2_LEVEL_DEBUG, "replacing header %s", hrI->name); headerreplace(hdI, hdNew); if (hdNew->prev == NULL) ctx->msg->hdFirst = hdNew; } else { logbook(ctx->l2, L2_LEVEL_DEBUG, "appending header %s", hrI->name); for (hdI = ctx->msg->hdFirst; hdI->next != NULL; hdI = hdI->next); hdI->next = hdNew; hdNew->prev = hdI; } } } #if 0 { /* TODO debug only - wait for l2 to support code block bypassing */ int i; headerrule_t *hrD; headerdata_t *hdD; logbook(ctx->l2, L2_LEVEL_DEBUG, "headerrewrite output"); for (hrD = ctx->option_firstheaderrule; hrD != NULL; hrD = hrD->next) logbook(ctx->l2, L2_LEVEL_DEBUG, "hrD->header=%s", hrD->header); for (hdD = ctx->msg->hdFirst; hdD != NULL; hdD = hdD->next) { if (hdD->ndata == 0) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name=%s: (NO DATA)", hdD->name); if (hdD->ndata == 1) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name:hdD->data.s %s %s", hdD->name, hdD->data.s); if (hdD->ndata > 1) for (i = 0; i < hdD->ndata; i++) logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD->name:hdD->data.m[%d] %s %s", i, hdD->name, hdD->data.m[i]); } } #endif } @ 1.42 log @removal of dormant OSSP tai library @ text @d31 1 @ 1.41 log @correctly handle and detect empty newsgroups @ text @a34 1 #include "tai.h" @ 1.40 log @on last run, str_parse() returns empty string, not NULL, as remainder, so free(3) it @ text @d246 1 a246 1 if (msg->azNewsgroups == NULL) @ 1.39 log @fix iteration logic @ text @d44 1 a44 1 #if defined(HAVE_DMALLOC_H) && defined(DMALLOC) d187 1 @ 1.38 log @defeat leakage when tear down headermatrix; iterate using lookahead @ text @d730 6 a735 14 if (hdI->name == NULL || strlen(hdI->name) == 0 || hdI->ndata == 0) continue; if (hdI->ndata == 0) { logbook(msg->l2, L2_LEVEL_DEBUG, "header=%s, no data", hdI->name); } else if(hdI->ndata == 1) { /* header data is single valued */ logbook(msg->l2, L2_LEVEL_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++) { logbook(msg->l2, L2_LEVEL_DEBUG, "header=%s[%d], data=%s", hdI->name, i, hdI->data.m[i]); d737 9 a745 1 argz_add(&msg->azHeaders, &msg->asHeaders, hdI->data.m[i]); @ 1.37 log @extend copyright messages based on CVS information @ text @d720 1 d727 2 a728 1 for (hdI = msg->hdFirst; hdI != NULL; hdI = hdI->next) { /* for each header */ d748 6 @ 1.36 log @moving definitions between header files @ text @d3 3 a5 3 ** Copyright (c) 2002-2003 Ralf S. Engelschall ** Copyright (c) 2002-2003 The OSSP Project ** Copyright (c) 2002-2003 Cable & Wireless Germany @ 1.35 log @header rewriting engine was integrated through lmtp2nntp_common.h @ text @d34 1 a34 1 #include "lmtp2nntp_common.h" /* FIMXE logbook only */ @ 1.34 log @disable function to get rid of nasty compiler warning @ text @a456 2 /* FIXME below is the header rewriting engine which must be cleaned up and integrated */ @ 1.33 log @remove trailing whitespaces from source tree @ text @d498 1 d509 1 @ 1.32 log @Apply the standard OSSP copyright header, document ASCII art and fix URL to lmtp2nntp homepage. @ text @d137 1 a137 1 * d542 1 a542 1 var_t *var, void *_ctx, d642 1 a642 1 else d787 1 a787 1 @ 1.31 log @Implement the decision from yesterday evening and finally rename fixme.h to lmtp2nntp_common.h because there is little chance this this stuff ever can be moved into more local source contexts. @ text @d2 4 a5 2 ** Copyright (c) 2001-2002 The OSSP Project ** Copyright (c) 2001-2002 Cable & Wireless Deutschland d9 1 a9 1 ** It can be found at http://www.ossp.org/pkg/lmtp2nntp/. d26 1 a26 1 ** msg.c: mail message manipulation library @ 1.30 log @canonifydate requires a more complete lib_tai and is rescheduled for a later release @ text @d32 1 a32 1 #include "fixme.h" /* FIMXE logbook only */ @ 1.29 log @get rid of all warnings under gcc 3.1 @ text @a564 122 static var_rc_t canonifydate( val_t *prival, const char *cpArg, size_t nArg, const char *cpVal, size_t nVal, char **cppOut, size_t *pnOut, size_t *pnOutsize) { /* RFC0822 5. DATE AND TIME SPECIFICATION 5.1. SYNTAX date-time = [ day "," ] date time ; "dd mm yy" or "hh:mm:ss zzz" day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" date = 1*2DIGIT month 2DIGIT ; day month year e.g. 20 Jun 82 month = "Jan" / "Feb"/ "Mar" / "Apr"/ "May" / "Jun"/ "Jul" / "Aug"/ "Sep" / "Oct"/ "Nov" / "Dec" time = hour zone ; ANSI and Military hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59 zone = "UT" / "GMT" ; Universal Time, North American : UT / "EST" / "EDT" ; Eastern: - 5/ - 4 / "CST" / "CDT" ; Central: - 6/ - 5 / "MST" / "MDT" ; Mountain: - 7/ - 6 / "PST" / "PDT" ; Pacific: - 8/ - 7 / 1ALPHA ; Military: Z = UT; A:-1; (J not used); M:-12; N:+1; Y:+12 / ( ("+" / "-") 4DIGIT ) ; Local differential hours+min. (HHMM) */ tai_rc_t rv; int i; int bOk; char *fmt[] = { "%a, %d %b %Y %H:%M:%S %z", /* RFC0822 but four digit year */ "%a, %d %b %y %H:%M:%S %z", /* RFC0822 strict */ "%a, %d %b %Y %H:%M %z", /* RFC0822 but four digit year */ "%a, %d %b %y %H:%M %z", /* RFC0822 strict */ "%d %b %Y %H:%M:%S %z", /* RFC0822 but four digit year */ "%d %b %y %H:%M:%S %z", /* RFC0822 strict */ "%d %b %Y %H:%M %z", /* RFC0822 but four digit year */ "%d %b %y %H:%M %z", /* RFC0822 strict */ "%a %b %d %H:%M:%S %Y", /* strange Mon Jan 27 12:34:56 2001 */ NULL }; tai_t *now; tai_t *pastrange; tai_t *futurerange; tai_t *value; tai_create(&now); /* FIXME ex */ (void /*FIXME*/)tai_import(now, TAI_TYPE_UNIX); /* parse argument ([past][,[future]]) () (,) infinite range in the past, infinite range in the future => canonify only (7), (7,) seven days in the past, infinite range in the future (,3) infinite range in the past, three days in the future (7,0) seven days in the past, no point in future allowed (0,3) no point in past allowed, three days in the future => useless (0,0) now */ pastrange = NULL; /* infinite */ futurerange = NULL; /* infinite */ if (cpArg != NULL) { char *cpP, *cpF; cpP = strdupex(cpArg); cpF = strchr(cpP, ','); if (cpF == NULL) cpF = ""; else *cpF++ = NUL; if (strlen(cpP) != 0) { tai_create(&pastrange); /* FIXME ex */ (void /*FIXME*/)tai_import(pastrange , TAI_TYPE_SECONDS, 24*60*60*atoi(cpP)); } if (strlen(cpF) != 0) { tai_create(&futurerange); /* FIXME ex */ (void /*FIXME*/)tai_import(futurerange, TAI_TYPE_SECONDS, 24*60*60*atoi(cpF)); } free(cpP); } /* FIXME printf("DEBUG: cpVal=\"%41s\", cpArg=\"%s\"\n", cpVal, cpArg); */ if ((cpVal == NULL) || (strlen(cpVal) == 0)) { *cppOut = (char *)mallocex(DATELENMAX); *pnOutsize = DATELENMAX; (void /*FIXME*/)tai_format(now, *cppOut, DATELENMAX, "%a, %d %b %Y %H:%M:%S %z"); *pnOut = strlen(*cppOut); } else { tai_create(&value); /* FIXME ex */ bOk = FALSE; for (i = 0; !bOk && (fmt[i] != NULL); i++) { if ((rv = tai_parse(value, cpVal, strlen(cpVal), fmt[i])) == TAI_OK) bOk = TRUE; /* FIXME printf("DEBUG: checked against \"%41s\" returned %d\n", fmt[i], rv); */ } if ( bOk #if 0 tai_op is not yet implemented && ((pastrange != NULL) && (tai_op(value, TAI_OP_GT, pastrange ) == TAI_OK)) && ((futurerange != NULL) && (tai_op(value, TAI_OP_LT, futurerange) == TAI_OK)) #endif ) { *cppOut = (char *)mallocex(DATELENMAX); *pnOutsize = DATELENMAX; (void /*FIXME*/)tai_format(value, *cppOut, DATELENMAX, "%a, %d %b %Y %H:%M:%S %z"); *pnOut = strlen(*cppOut); } else { *cppOut = ""; *pnOutsize = 0; *pnOut = 0; } } /* cleanup */ if(now != NULL) tai_destroy(now); if(pastrange != NULL) tai_destroy(pastrange); if(futurerange != NULL) tai_destroy(futurerange); if(value != NULL) tai_destroy(value); return VAR_OK; } a638 3 } else if (op_len == 12 && strncmp(op_ptr, "canonifydate", 12) == 0) { return canonifydate (prival, arg_ptr, arg_len, val_ptr, val_len, out_ptr, out_len, out_size); @ 1.28 log @remove lot of hardcoded header handling; user can now shoot himself in foot @ text @d32 1 a32 1 #include "fixme.h" //FIMXE logbook only d455 1 a455 1 //FIXME below is the header rewriting engine which must be cleaned up and integrated d608 1 a608 1 tai_create(&now); //FIXME ex d630 1 a630 1 tai_create(&pastrange); //FIXME ex d634 1 a634 1 tai_create(&futurerange); //FIXME ex d640 1 a640 1 //FIXME printf("DEBUG: cpVal=\"%41s\", cpArg=\"%s\"\n", cpVal, cpArg); d648 1 a648 1 tai_create(&value); //FIXME ex d653 1 a653 1 //FIXME printf("DEBUG: checked against \"%41s\" returned %d\n", fmt[i], rv); d891 1 a891 1 { //TODO debug only - wait for l2 to support code block bypassing d926 1 a926 1 { //TODO debug only - wait for l2 to support code block bypassing d935 1 a935 1 //logbook(ctx->l2, L2_LEVEL_DEBUG, "hdD=%.8lx, hdD->name=%.8lx, hdD->data.s=%.8lx", (long)hdD, (long)&hdD->name, (long)&hdD->data.s); a953 1 char *cp; d1070 1 a1070 1 { //TODO debug only - wait for l2 to support code block bypassing @ 1.27 log @not setting free()d resources to 0/NULL caused segfaults @ text @d162 1 a162 1 logbook(msg->l2, L2_LEVEL_DEBUG, "replace envelope From w/o colon by X-F: pseudotag"); a164 2 * After splitting headers into name and value pairs this envelope ist * stripped off. d167 1 a167 1 memcpy(cpHeaders, "X-F:", 4); a186 1 msg->cpMsgid = NULL; d191 1 a191 11 logbook(msg->l2, L2_LEVEL_DEBUG, "processing header \"%s\"", cp); if (strcasecmp("X-F:", cp) == 0) { argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ continue; } if (strcasecmp("Path:", cp) == 0) { argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ continue; } a199 21 if (strcasecmp("To:", cp) == 0) { argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ continue; } if (strcasecmp("Cc:", cp) == 0) { argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ continue; } if (strcasecmp("Message-ID:", cp) == 0) { if (msg->cpMsgid != NULL) return MSG_ERR_SPLITIDMULTI; argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ if ((cp == NULL) || (strlen(cp) == 0)) /* get value */ return MSG_ERR_SPLITIDEMPTY; if ((msg->cpMsgid = strdup(cp)) == NULL) return MSG_ERR_MEM; argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ continue; } d201 2 a202 1 argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del name */ d205 2 a206 1 argz_delete(&msg->azHeaders, &msg->asHeaders, cp); /* del value */ a214 4 logbook(msg->l2, L2_LEVEL_DEBUG, "checking Message-ID"); if (msg->cpMsgid == NULL) return MSG_ERR_SPLITIDNONE; a221 4 logbook(msg->l2, L2_LEVEL_DEBUG, "adding mandatory Path: header"); argz_add(&msg->azHeaders, &msg->asHeaders, "Path:"); argz_add(&msg->azHeaders, &msg->asHeaders, "lmtp2nntp!not-for-mail"); d252 21 a276 2 argz_add(&msg->azHeaders, &msg->asHeaders, "Message-ID:"); argz_add(&msg->azHeaders, &msg->asHeaders, msg->cpMsgid); a446 3 else if (rc == MSG_ERR_SPLITIDNONE ) str = "MSG: header is missing 'Message-ID'"; else if (rc == MSG_ERR_SPLITIDEMPTY ) str = "MSG: header has empty 'Message-ID'"; else if (rc == MSG_ERR_SPLITIDMULTI ) str = "MSG: header has multiple 'Message-ID's"; d451 1 d704 7 a743 6 if (msg != NULL) { if (msg->cpMsgid != NULL) free(msg->cpMsgid); msg->cpMsgid = strdupex(cp); } d759 1 a759 7 if (val_ptr == NULL) { *out_ptr = ""; *out_len = 0; *out_size = 0; return VAR_OK; } else if (op_len == 15 && strncmp(op_ptr, "createmessageid", 15) == 0) { @ 1.26 log @unused variable @ text @d491 1 d493 1 a493 1 if (hdC->ndata == 1) d496 2 d502 1 a502 1 if (hdC->name != NULL) d504 2 a1000 1 hdNew->ndata = 0; a1009 1 hdNew->ndata = 0; a1033 1 hdNew->ndata = 0; a1055 1 hdNew->ndata = 0; a1068 1 hdNew->ndata = 0; @ 1.25 log @tai debugging @ text @a774 1 int i; @ 1.24 log @clean up data.[sm] @ text @d658 1 a658 1 printf("DEBUG: cpVal=\"%41s\", cpArg=\"%s\"\n", cpVal, cpArg); @ 1.23 log @rename @ text @d480 1 a480 1 static void headerdestroy(headerdata_t *hdC) d495 4 d501 6 d995 2 a996 2 hdNew->name = NULL; /*FIXME clean up data.s and data.m */ d1005 2 a1006 1 /*FIXME clean up data.s and data.m */ d1030 2 a1031 1 /*FIXME clean up data.s and data.m */ a1039 1 /*FIXME clean up data.m */ d1053 2 a1054 1 /*FIXME clean up data.s and data.m */ a1057 1 /*FIXME clean up data.m */ d1067 2 a1068 1 /*FIXME clean up data.s and data.m */ d1072 1 @ 1.22 log @it is a good place here since code and callback are both in this file @ text @d978 3 a980 3 logbook(ctx->l2, L2_LEVEL_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) { logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->header, var_strerror(ctx->config_varregex, var_rc, &cp) == VAR_OK ? cp : "Unknown Error", var_rc); d985 1 a985 1 hdNew->name = NULL; //FIXME rename ->header to ->name d990 1 a990 1 hdNew->name = res_ptr; //FIXME rename ->header to ->name d1037 1 a1037 1 logbook(ctx->l2, L2_LEVEL_DEBUG, "rule has no regex but static header %s", hrI->header); d1039 1 a1039 1 hdNew->name = strdupex(hrI->header); //FIXME rename ->header to ->name d1067 2 a1068 2 logbook(ctx->l2, L2_LEVEL_DEBUG, "hrI->header=%s, hdI->name=%s", hrI->header, hdI->name); if (strcasecmp(hrI->header, hdI->name) == 0) d1072 1 a1072 1 logbook(ctx->l2, L2_LEVEL_DEBUG, "replacing header %s", hrI->header); d1078 1 a1078 1 logbook(ctx->l2, L2_LEVEL_DEBUG, "appending header %s", hrI->header); @ 1.21 log @kick off the bad name, use less variables, fix wrong variable in logs @ text @d934 1 a934 1 if ((var_rc = var_config(ctx->config_varctx, VAR_CONFIG_CB_OPERATION, operate_cb, ctx->prival)) != VAR_OK) { //FIXME isn't main a better place to do this? @ 1.20 log @a hash could be implemented to enhance performance here @ text @d897 2 a898 1 var_rc_t rc; char *cp; //FIXME what a bad name, it's not the returncode of this function d930 2 a931 2 if ((rc = var_config(ctx->config_varregex, VAR_CONFIG_CB_VALUE, regex_lookup, regex_ctx)) != VAR_OK) { logbook(ctx->l2, L2_LEVEL_ERROR, "configure regex callback failed with %s (%d)", var_strerror(ctx->config_varregex, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); d934 2 a935 2 if ((rc = var_config(ctx->config_varctx, VAR_CONFIG_CB_OPERATION, operate_cb, ctx->prival)) != VAR_OK) { //FIXME isn't main a better place to do this? logbook(ctx->l2, L2_LEVEL_ERROR, "configure operate callback failed with %s (%d)", var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); a976 1 var_rc_t var_rc; d980 1 a980 1 logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->header, var_strerror(ctx->config_varregex, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); a1000 1 var_rc_t var_rc; d1004 1 a1004 1 logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varregex, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); a1010 1 var_rc_t var_rc; d1014 1 a1014 1 logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", cp, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); a1047 1 var_rc_t var_rc; d1051 1 a1051 1 logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); @ 1.19 log @disable debug code blocks; request to enhance l2; FIXME/TODO statement @ text @d797 1 a797 1 /*FIXME we want O(1) here */ @ 1.18 log @changed from coder to user type logging @ text @d903 2 a904 1 { //FIXME debug code block d909 1 a909 1 logbook(ctx->l2, L2_LEVEL_DEBUG, "FIXME trace ---------- headerrewrite() ----------"); d922 1 d938 2 a939 1 { //FIXME debug code block d944 1 a944 1 logbook(ctx->l2, L2_LEVEL_DEBUG, "FIXME trace ---------- headerrewrite() ---------- MIDDLE"); d958 1 d1077 1 a1077 2 if (hdNew->prev == NULL) { logbook(ctx->l2, L2_LEVEL_DEBUG, "FIXME trace #1"); a1078 1 } d1089 2 a1090 1 { //FIXME debug code block d1095 1 a1095 1 logbook(ctx->l2, L2_LEVEL_DEBUG, "FIXME trace ---------- headerrewrite() ---------- FINISH"); d1108 1 @ 1.17 log @data.m and .s are unions, so cpOld is really needed but strdup is not @ text @d791 2 d795 1 d852 3 d863 2 a864 2 for (hdI = msg->hdFirst; hdI != NULL; hdI = hdI->next) { /* for each matrix header */ logbook(msg->l2, L2_LEVEL_DEBUG, "FIXME trace loop hdI=%.8lx, hI->name=\"%s\"", hdI, hdI->name); @ 1.16 log @setting size to zero makes lib_var assume static outbuffer @ text @d824 1 a824 1 hdI->data.m[0] = strdupex(cpOld); //FIXME @ 1.15 log @createmessageid now using lib_val; added support for long nodenames @ text @d676 1 a676 1 *cppOut = (char *)mallocex(0); d768 2 a769 2 *out_ptr = ""; //FIXME will lib_val try to free() this? *out_len = 0; @ 1.14 log @renamed and relocated @ text @a33 3 #include //FIXME createmessageid() hack d46 1 a46 1 msg_t *msg_create(void) d48 1 d51 3 d72 9 d109 2 d574 1 d696 1 d702 12 a713 1 static int mcounter = 0; //FIXME no statics in this program! d715 26 a740 3 struct utsname name; //FIXME this must be taken from global - requires context if (uname(&name) == -1) { memcpy(name.nodename, "localhost.invalid", strlen("localhost.invalid") + 1); d742 2 d745 5 a749 2 /* idea and first implementation as a contribution to lmtp2nntp v1.0.0 by Christos Ricudis */ a750 7 cp = (char *)malloc( 100); //FIXME how much exacly? str_format(cp, 100, "<%0.11d$%0.6d$%0.2d$%.20s$@@%.40s>", time(NULL), getpid(), mcounter++, "noFid" /*FIXME ((msg->cpFid == NULL)?"noFid":(msg->cpFid))*/, name.nodename); /*if (msg->cpMsgid != NULL) free(msg->cpMsgid); msg->cpMsgid = cp; //FIXME what about aligning lib_val here? */ d758 1 a758 1 var_t *var, void *ctx, d764 1 d768 1 a768 1 *out_ptr = ""; a772 7 if (op_len == 6 && strncmp(op_ptr, "return", 6) == 0) { //FIXME needless block *out_ptr = malloc(arg_len); *out_len = arg_len; *out_size = arg_len; memcpy(*out_ptr, arg_ptr, arg_len); return VAR_OK; } d774 1 a774 1 return createmessageid(arg_ptr, arg_len, val_ptr, val_len, out_ptr, out_len, out_size); d777 1 a777 1 return canonifydate(arg_ptr, arg_len, val_ptr, val_len, out_ptr, out_len, out_size); d925 1 a925 1 if ((rc = var_config(ctx->config_varctx, VAR_CONFIG_CB_OPERATION, operate_cb, NULL)) != VAR_OK) { //FIXME isn't main a better place to do this? @ 1.13 log @yes, at least on FreeBSD, LINUX and Solaris8 @ text @a560 1 #define MAXOUT 32 /* strlen("Mon, 31 Jul 2002 12:34:56 +0123")+1 */ d637 3 a639 3 *cppOut = (char *)mallocex(MAXOUT); *pnOutsize = MAXOUT; (void /*FIXME*/)tai_format(now, *cppOut, MAXOUT, "%a, %d %b %Y %H:%M:%S %z"); d657 3 a659 3 *cppOut = (char *)mallocex(MAXOUT); *pnOutsize = MAXOUT; (void /*FIXME*/)tai_format(value, *cppOut, MAXOUT, "%a, %d %b %Y %H:%M:%S %z"); a680 1 #undef MAXOUT //FIXME is there a better way to do it? @ 1.12 log @lib_var experts, please review @ text @d664 1 a664 1 *cppOut = (char *)mallocex(0); //FIXME is this portable? @ 1.11 log @relocation to new home @ text @a663 6 /*FIXME interesting lib_var behaviour: *cppOut = NULL; returning NULL with size/out=0 means not expandable and keeps the current value *cppOut = (char *)mallocex(0); returning any freeable pointer with size/out=0 means empty value *pnOutsize = 0; *pnOut = 0; */ @ 1.10 log @added %createmessageid header rewrite function. This a dirty hack. @ text @a534 9 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; }; @ 1.9 log @implemented %canonifydate so far as lib_tai currently permits @ text @d34 2 d699 29 d743 1 a743 1 if (op_len == 6 && strncmp(op_ptr, "return", 6) == 0) { d750 2 a751 7 else if (op_len == 5 && strncmp(op_ptr, "upper", 5) == 0) { *out_ptr = malloc(val_len); *out_len = val_len; *out_size = val_len; for (i = 0; i < val_len; i++) (*out_ptr)[i] = (char)toupper((int)(val_ptr[i])); return VAR_OK; @ 1.8 log @flush canonifydate experiment @ text @d568 5 a572 1 static void canonifydate(const char *pVal, const char *pArg) a591 1 tai_t *tm; a592 1 char out[32]; d594 1 a594 3 time_t tim; const struct tm *sttm; int ok; d606 4 d611 19 a629 9 printf("DEBUG: pVal=\"%41s\", pArg=\"%s\" ", pVal, pArg); tai_create(&tm); tim = time(NULL); sttm = localtime(&tim); ok = 0; for (i = 0; !ok && (fmt[i] != NULL); i++) { //printf("DEBUG: date=%s, fmt[%d]=%30s ", pArg, i, fmt[i]); if ((rv = tai_parse(tm, pVal, strlen(pVal), fmt[i])) != TAI_OK) ;//printf("FAILED tai_parse() returned %d", rv); d631 28 a658 1 ok = 1; d660 10 d671 9 a679 4 if ((rv = tai_format(tm, out, sizeof(out), fmt[i])) != TAI_OK) printf("#%d: tai_format() returned %d", i, rv); if (strcmp(pArg, out) != 0) printf("#%d: output \"%s\", expected \"%s\" (input)", i, out, pArg); a680 2 #endif //printf("\n"); d682 12 a693 7 if (ok) { rv = tai_format(tm, out, sizeof(out), "%a, %d %b %Y %H:%M:%S %z"); printf("OK[%d], %s (%d)\n", ok, out, rv); } else printf("FAILED\n"); tai_destroy(tm); d695 1 d728 1 a728 7 *out_ptr = malloc(val_len); *out_len = val_len; *out_size = val_len; canonifydate(val_ptr, arg_ptr); for (i = 0; i < val_len; i++) (*out_ptr)[i] = (char)tolower((int)(val_ptr[i])); return VAR_OK; @ 1.7 log @use correct context for var_strerror @ text @d33 1 d568 69 a645 1 fprintf(stderr, "DEBUG: #1 HALLO\n"); d667 1 a667 1 else if (op_len == 5 && strncmp(op_ptr, "lower", 5) == 0) { d671 1 @ 1.6 log @put it in the right context. Thanks, rse @ text @d749 1 a749 1 logbook(ctx->l2, L2_LEVEL_ERROR, "configure regex callback failed with %s (%d)", var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); d797 1 a797 1 logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->header, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); d822 1 a822 1 logbook(ctx->l2, L2_LEVEL_ERROR, "expansion of '%s' failed: %s", hrI->val, var_strerror(ctx->config_varctx, rc, &cp) == VAR_OK ? cp : "Unknown Error", rc); @ 1.5 log @no change, only a code move @ text @d576 1 d752 1 a752 1 if ((rc = var_config(ctx->config_varregex, VAR_CONFIG_CB_OPERATION, operate_cb, NULL)) != VAR_OK) { @ 1.4 log @move from log macro to logbook function @ text @d463 463 @ 1.3 log @added --testfile option for header rewrite testing. Transformation from argz to linked list of elements and back implemented for integration into the gateway's "inner loop". Have trouble with lib_ex segfaulting when throwing exception and tried to track down this issue with no luck. @ text @d32 1 d140 1 a140 1 log0(msg, DEBUG, "split message into header and body"); d147 1 a147 1 log0(msg, DEBUG, "replace envelope From w/o colon by X-F: pseudotag"); d156 1 a156 1 log0(msg, DEBUG, "unwrap header lines"); d163 1 a163 1 log0(msg, DEBUG, "split header lines into names and values"); d173 1 a173 1 log0(msg, DEBUG, "check for headers we care about and do whatever neccessary"); d179 1 a179 1 log1(msg, DEBUG, "processing header \"%s\"", cp); d194 1 a194 1 log1(msg, DEBUG, "found foreign-ID \"%s\" for logging", msg->cpFid); d232 1 a232 1 log0(msg, DEBUG, "checking Message-ID"); d236 1 a236 1 log0(msg, DEBUG, "checking Newsgroups"); d243 1 a243 1 log0(msg, DEBUG, "adding mandatory Path: header"); d247 1 a247 1 log0(msg, DEBUG, "split complete"); d267 1 a267 1 log0(msg, DEBUG, "verify Newsgroups"); d276 1 a276 1 log0(msg, DEBUG, "verify Message-ID"); d284 1 a284 1 log0(msg, DEBUG, "merge name/value pairs into single string"); d302 1 a302 1 log0(msg, DEBUG, "fold headers"); d352 1 a352 1 log2(msg, DEBUG, "a folded header \"%{text}D\"", cpWrap, strlen(cpWrap)); d357 1 a357 1 log2(msg, DEBUG, "verbatim header \"%{text}D\"", cp, strlen(cp)); d364 1 a364 1 log0(msg, DEBUG, "strigify headers"); d372 1 a372 1 log0(msg, DEBUG, "assemble header and body"); d440 1 a440 1 log0(msg, DEBUG, "join complete"); @ 1.2 log @bump copyright year @ text @d57 1 @ 1.1 log @Mega-Commit: Finally restructure the lmtp2nntp source tree in order to clean it up. We especially use a consistent prefix for all inlined sources. @ text @d2 2 a3 2 ** Copyright (c) 2001 The OSSP Project ** Copyright (c) 2001 Cable & Wireless Deutschland @