#include "config.h" #include "search.h" #include "message.h" #include "messageinfo.h" #include "rematchstr.h" #include "rematchmsg.h" #include "varlist.h" #include #include #include void Search::cleanup() { if (pcre_regexp_extra) { pcre_free(pcre_regexp_extra); pcre_regexp_extra=NULL; } if (pcre_regexp) { pcre_free(pcre_regexp); pcre_regexp=NULL; } if (pcre_vectors) { free(pcre_vectors); pcre_vectors=NULL; } } int Search::init(const char *expr, const char *opts) { match_top_header=0; match_other_headers=0; match_body=0; weight1=1; weight2=1; scoring_match=0; score=0; if (strchr(opts, 'h')) match_top_header=match_other_headers=1; if (strchr(opts, 'H')) match_top_header=1; if (strchr(opts, 'b')) match_body=1; if (!match_top_header && !match_other_headers && !match_body) { match_top_header=1; match_other_headers=1; if (strchr(opts, 'w')) match_body=1; } const char *errptr; cleanup(); int errindex; pcre_regexp=pcre_compile(expr, PCRE_UTF8 | (strchr(opts, 'D') ? 0:PCRE_CASELESS), &errptr, &errindex, 0); if (!pcre_regexp) { Buffer b; b="Invalid regular expression, offset "; b.append((unsigned long)errindex); b += " of: "; b += expr; b += ": "; b += errptr; b += "\n"; b += '\0'; merr.write(b); return -1; } pcre_regexp_extra=pcre_study(pcre_regexp, 0, &errptr); if (errptr) { Buffer b; b="Error parsing regular expression: "; b += expr; b += ": "; b += errptr; b += "\n"; b += '\0'; merr.write(b); return -1; } search_expr=expr; int cnt=0; pcre_fullinfo(pcre_regexp, pcre_regexp_extra, PCRE_INFO_CAPTURECOUNT, &cnt); pcre_vector_count=(cnt+1)*3; pcre_vectors=(int *)malloc(pcre_vector_count*sizeof(int)); if (!pcre_vectors) { Buffer b; b=strerror(errno); b += "\n"; b += '\0'; merr.write(b); return -1; } while (*opts) { if (*opts == '.' || isdigit(*opts) || *opts == '-' || *opts == '+') { weight1=atof(opts); while (*opts && *opts != ',') ++opts; if (*opts == ',') { ++opts; if (*opts == '.' || isdigit(*opts) || *opts == '-' || *opts == '+') weight2=atof(opts); } scoring_match=1; break; } ++opts; } return (0); } int Search::find(Message &msg, MessageInfo &, const char *expr, const char *opts, Buffer *foreachp) { if (init(expr, opts)) return (-1); msg.Rewind(); return (findinline(msg, expr, foreachp)); } int Search::find(const char *str, const char *expr, const char *opts, Buffer *foreachp) { if (init(expr, opts)) return (-1); if (VerboseLevel() > 2) { Buffer msg; msg="Matching /"; msg.append(expr); msg.append("/ against "); msg += str; msg += '\n'; msg += '\0'; merr.write(msg); } int startoffset=0; const char *orig_str=str; int match_count=0; for (;;) { match_count=pcre_exec(pcre_regexp, pcre_regexp_extra, orig_str, strlen(orig_str), startoffset, 0, pcre_vectors, pcre_vector_count); if (match_count <= 0) break; startoffset=pcre_vectors[1]; score += weight1; weight1 *= weight2; if (!scoring_match || foreachp) { init_match_vars(orig_str, match_count, pcre_vectors, foreachp); if (!foreachp) break; } } return (0); } ////////////////////////////////////////////////////////////////////////////// // // Search individual lines for the pattern (transparently concatenate // continued headers. // ////////////////////////////////////////////////////////////////////////////// int Search::findinline(Message &msg, const char *expr, Buffer *foreachp) { struct rfc2045_decodemsgtoutf8_cb decode_cb; memset(&decode_cb, 0, sizeof(decode_cb)); if (!match_top_header && !match_other_headers) decode_cb.flags |= RFC2045_DECODEMSG_NOHEADERS; else if (match_top_header && !match_other_headers) decode_cb.flags |= RFC2045_DECODEMSG_NOATTACHHEADERS; if (!match_body) decode_cb.flags |= RFC2045_DECODEMSG_NOBODY; current_line.reset(); decode_cb.output_func=&Search::search_cb; decode_cb.arg=this; foreachp_arg=foreachp; rfc2045_decodemsgtoutf8(&msg.rfc2045src_parser, msg.rfc2045p, &decode_cb); if (current_line.Length() >= 1) search_cb("\n", 1); return 0; } int Search::search_cb(const char *ptr, size_t cnt, void *arg) { return ((Search *)arg)->search_cb(ptr, cnt); } int Search::search_cb(const char *ptr, size_t cnt) { while (cnt) { size_t i; if (*ptr == '\n') { current_line += '\0'; if (VerboseLevel() > 2) { Buffer msg; msg="Matching /"; { Buffer cpy; cpy += search_expr; cpy += '\0'; msg.append(cpy); } msg.append("/ against "); msg += current_line; msg.pop(); // Trailing null byte. msg += '\n'; msg += '\0'; merr.write(msg); } const char *orig_str=current_line; int match_count; match_count=pcre_exec(pcre_regexp, pcre_regexp_extra, orig_str, strlen(orig_str), 0, 0, pcre_vectors, pcre_vector_count); if (match_count > 0) { score += weight1; weight1 *= weight2; if (!scoring_match || foreachp_arg) { init_match_vars(orig_str, match_count, pcre_vectors, foreachp_arg); if (!foreachp_arg) // Stop searching now return (-1); } } else if (VerboseLevel() > 2) merr.write("Not matched.\n"); current_line.reset(); ++ptr; --cnt; continue; } for (i=0; i