123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869 |
- /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 2002-2004 by the Claws Mail Team and Hiroyuki Yamamoto
- *
- * 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 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 program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- #ifdef HAVE_CONFIG_H
- # include "config.h"
- #endif
- #include <glib.h>
- #include <glib/gi18n.h>
- #include <ctype.h>
- #include <string.h>
- #include <stdlib.h>
- #include <errno.h>
- #ifdef USE_PTHREAD
- #include <pthread.h>
- #endif
- #include "defs.h"
- #include "utils.h"
- #include "procheader.h"
- #include "matcher.h"
- #include "matcher_parser.h"
- #include "prefs_gtk.h"
- #include "addr_compl.h"
- #include "codeconv.h"
- #include "quoted-printable.h"
- #include "claws.h"
- #include <ctype.h>
- /*!
- *\brief Keyword lookup element
- */
- struct _MatchParser {
- gint id; /*!< keyword id */
- gchar *str; /*!< keyword */
- };
- typedef struct _MatchParser MatchParser;
- /*!
- *\brief Table with strings and ids used by the lexer and
- * the parser. New keywords can be added here.
- */
- static const MatchParser matchparser_tab[] = {
- /* msginfo flags */
- {MATCHCRITERIA_ALL, "all"},
- {MATCHCRITERIA_UNREAD, "unread"},
- {MATCHCRITERIA_NOT_UNREAD, "~unread"},
- {MATCHCRITERIA_NEW, "new"},
- {MATCHCRITERIA_NOT_NEW, "~new"},
- {MATCHCRITERIA_MARKED, "marked"},
- {MATCHCRITERIA_NOT_MARKED, "~marked"},
- {MATCHCRITERIA_DELETED, "deleted"},
- {MATCHCRITERIA_NOT_DELETED, "~deleted"},
- {MATCHCRITERIA_REPLIED, "replied"},
- {MATCHCRITERIA_NOT_REPLIED, "~replied"},
- {MATCHCRITERIA_FORWARDED, "forwarded"},
- {MATCHCRITERIA_NOT_FORWARDED, "~forwarded"},
- {MATCHCRITERIA_LOCKED, "locked"},
- {MATCHCRITERIA_NOT_LOCKED, "~locked"},
- {MATCHCRITERIA_COLORLABEL, "colorlabel"},
- {MATCHCRITERIA_NOT_COLORLABEL, "~colorlabel"},
- {MATCHCRITERIA_IGNORE_THREAD, "ignore_thread"},
- {MATCHCRITERIA_NOT_IGNORE_THREAD, "~ignore_thread"},
- /* msginfo headers */
- {MATCHCRITERIA_SUBJECT, "subject"},
- {MATCHCRITERIA_NOT_SUBJECT, "~subject"},
- {MATCHCRITERIA_FROM, "from"},
- {MATCHCRITERIA_NOT_FROM, "~from"},
- {MATCHCRITERIA_TO, "to"},
- {MATCHCRITERIA_NOT_TO, "~to"},
- {MATCHCRITERIA_CC, "cc"},
- {MATCHCRITERIA_NOT_CC, "~cc"},
- {MATCHCRITERIA_TO_OR_CC, "to_or_cc"},
- {MATCHCRITERIA_NOT_TO_AND_NOT_CC, "~to_or_cc"},
- {MATCHCRITERIA_AGE_GREATER, "age_greater"},
- {MATCHCRITERIA_AGE_LOWER, "age_lower"},
- {MATCHCRITERIA_NEWSGROUPS, "newsgroups"},
- {MATCHCRITERIA_NOT_NEWSGROUPS, "~newsgroups"},
- {MATCHCRITERIA_INREPLYTO, "inreplyto"},
- {MATCHCRITERIA_NOT_INREPLYTO, "~inreplyto"},
- {MATCHCRITERIA_REFERENCES, "references"},
- {MATCHCRITERIA_NOT_REFERENCES, "~references"},
- {MATCHCRITERIA_SCORE_GREATER, "score_greater"},
- {MATCHCRITERIA_SCORE_LOWER, "score_lower"},
- {MATCHCRITERIA_SCORE_EQUAL, "score_equal"},
- {MATCHCRITERIA_PARTIAL, "partial"},
- {MATCHCRITERIA_NOT_PARTIAL, "~partial"},
- {MATCHCRITERIA_FOUND_IN_ADDRESSBOOK, "found_in_addressbook"},
- {MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK, "~found_in_addressbook"},
- {MATCHCRITERIA_SIZE_GREATER, "size_greater"},
- {MATCHCRITERIA_SIZE_SMALLER, "size_smaller"},
- {MATCHCRITERIA_SIZE_EQUAL, "size_equal"},
- /* content have to be read */
- {MATCHCRITERIA_HEADER, "header"},
- {MATCHCRITERIA_NOT_HEADER, "~header"},
- {MATCHCRITERIA_HEADERS_PART, "headers_part"},
- {MATCHCRITERIA_NOT_HEADERS_PART, "~headers_part"},
- {MATCHCRITERIA_MESSAGE, "message"},
- {MATCHCRITERIA_NOT_MESSAGE, "~message"},
- {MATCHCRITERIA_BODY_PART, "body_part"},
- {MATCHCRITERIA_NOT_BODY_PART, "~body_part"},
- {MATCHCRITERIA_TEST, "test"},
- {MATCHCRITERIA_NOT_TEST, "~test"},
- /* match type */
- {MATCHTYPE_MATCHCASE, "matchcase"},
- {MATCHTYPE_MATCH, "match"},
- {MATCHTYPE_REGEXPCASE, "regexpcase"},
- {MATCHTYPE_REGEXP, "regexp"},
- /* actions */
- {MATCHACTION_SCORE, "score"}, /* for backward compatibility */
- {MATCHACTION_MOVE, "move"},
- {MATCHACTION_COPY, "copy"},
- {MATCHACTION_DELETE, "delete"},
- {MATCHACTION_MARK, "mark"},
- {MATCHACTION_UNMARK, "unmark"},
- {MATCHACTION_LOCK, "lock"},
- {MATCHACTION_UNLOCK, "unlock"},
- {MATCHACTION_MARK_AS_READ, "mark_as_read"},
- {MATCHACTION_MARK_AS_UNREAD, "mark_as_unread"},
- {MATCHACTION_FORWARD, "forward"},
- {MATCHACTION_FORWARD_AS_ATTACHMENT, "forward_as_attachment"},
- {MATCHACTION_EXECUTE, "execute"},
- {MATCHACTION_COLOR, "color"},
- {MATCHACTION_REDIRECT, "redirect"},
- {MATCHACTION_CHANGE_SCORE, "change_score"},
- {MATCHACTION_SET_SCORE, "set_score"},
- {MATCHACTION_STOP, "stop"},
- {MATCHACTION_HIDE, "hide"},
- {MATCHACTION_IGNORE, "ignore"},
- };
- enum {
- MATCH_ANY = 0,
- MATCH_ALL = 1,
- MATCH_ONE = 2
- };
- /*!
- *\brief Look up table with keywords defined in \sa matchparser_tab
- */
- static GHashTable *matchparser_hashtab;
- /*!
- *\brief Translate keyword id to keyword string
- *
- *\param id Id of keyword
- *
- *\return const gchar * Keyword
- */
- const gchar *get_matchparser_tab_str(gint id)
- {
- gint i;
- for (i = 0; i < sizeof matchparser_tab / sizeof matchparser_tab[0]; i++) {
- if (matchparser_tab[i].id == id)
- return matchparser_tab[i].str;
- }
- return NULL;
- }
- /*!
- *\brief Create keyword lookup table
- */
- static void create_matchparser_hashtab(void)
- {
- int i;
-
- if (matchparser_hashtab) return;
- matchparser_hashtab = g_hash_table_new(g_str_hash, g_str_equal);
- for (i = 0; i < sizeof matchparser_tab / sizeof matchparser_tab[0]; i++)
- g_hash_table_insert(matchparser_hashtab,
- matchparser_tab[i].str,
- (gpointer) &matchparser_tab[i]);
- }
- /*!
- *\brief Return a keyword id from a keyword string
- *
- *\param str Keyword string
- *
- *\return gint Keyword id
- */
- gint get_matchparser_tab_id(const gchar *str)
- {
- MatchParser *res;
- if (NULL != (res = g_hash_table_lookup(matchparser_hashtab, str))) {
- return res->id;
- } else
- return -1;
- }
- /* **************** data structure allocation **************** */
- /*!
- *\brief Allocate a structure for a filtering / scoring
- * "condition" (a matcher structure)
- *
- *\param criteria Criteria ID (MATCHCRITERIA_XXXX)
- *\param header Header string (if criteria is MATCHCRITERIA_HEADER
- or MATCHCRITERIA_FOUND_IN_ADDRESSBOOK)
- *\param matchtype Type of action (MATCHTYPE_XXX)
- *\param expr String value or expression to check
- *\param value Integer value to check
- *
- *\return MatcherProp * Pointer to newly allocated structure
- */
- MatcherProp *matcherprop_new(gint criteria, const gchar *header,
- gint matchtype, const gchar *expr,
- int value)
- {
- MatcherProp *prop;
- prop = g_new0(MatcherProp, 1);
- prop->criteria = criteria;
- prop->header = header != NULL ? g_strdup(header) : NULL;
- prop->expr = expr != NULL ? g_strdup(expr) : NULL;
- prop->matchtype = matchtype;
- prop->preg = NULL;
- prop->value = value;
- prop->error = 0;
- return prop;
- }
- /*!
- *\brief Free a matcher structure
- *
- *\param prop Pointer to matcher structure allocated with
- * #matcherprop_new
- */
- void matcherprop_free(MatcherProp *prop)
- {
- g_free(prop->expr);
- g_free(prop->header);
- if (prop->preg != NULL) {
- regfree(prop->preg);
- g_free(prop->preg);
- }
- g_free(prop);
- }
- /*!
- *\brief Copy a matcher structure
- *
- *\param src Matcher structure to copy
- *
- *\return MatcherProp * Pointer to newly allocated matcher structure
- */
- MatcherProp *matcherprop_copy(const MatcherProp *src)
- {
- MatcherProp *prop = g_new0(MatcherProp, 1);
-
- prop->criteria = src->criteria;
- prop->header = src->header ? g_strdup(src->header) : NULL;
- prop->expr = src->expr ? g_strdup(src->expr) : NULL;
- prop->matchtype = src->matchtype;
-
- prop->preg = NULL; /* will be re-evaluated */
- prop->value = src->value;
- prop->error = src->error;
- return prop;
- }
- /* ************** match ******************************/
- static gboolean match_with_addresses_in_addressbook
- (MatcherProp *prop, GSList *address_list, gint type,
- gchar* folderpath, gint match)
- {
- GSList *walk = NULL;
- gboolean found = FALSE;
- gchar *path = NULL;
- g_return_val_if_fail(address_list != NULL, FALSE);
- debug_print("match_with_addresses_in_addressbook(%d, %s)\n",
- g_slist_length(address_list), folderpath);
- if (folderpath == NULL ||
- strcasecmp(folderpath, _("Any")) == 0 ||
- *folderpath == '\0')
- path = NULL;
- else
- path = folderpath;
-
- start_address_completion(path);
- for (walk = address_list; walk != NULL; walk = walk->next) {
- /* exact matching of email address */
- guint num_addr = complete_address(walk->data);
- found = FALSE;
- if (num_addr > 1) {
- /* skip first item (this is the search string itself) */
- int i = 1;
- for (; i < num_addr && !found; i++) {
- gchar *addr = get_complete_address(i);
- extract_address(addr);
- if (strcasecmp(addr, walk->data) == 0)
- found = TRUE;
- g_free(addr);
- }
- }
- g_free(walk->data);
- if (match == MATCH_ALL) {
- /* if matching all addresses, stop if one doesn't match */
- if (!found)
- break;
- } else if (match == MATCH_ANY) {
- /* if matching any address, stop if one does match */
- if (found)
- break;
- }
- /* MATCH_ONE: there should be only one loop iteration */
- }
- end_address_completion();
-
- return found;
- }
- /*!
- *\brief Find out if a string matches a condition
- *
- *\param prop Matcher structure
- *\param str String to check
- *
- *\return gboolean TRUE if str matches the condition in the
- * matcher structure
- */
- static gboolean matcherprop_string_match(MatcherProp *prop, const gchar *str)
- {
- gchar *str1;
- gchar *str2;
- if (str == NULL)
- return FALSE;
- switch (prop->matchtype) {
- case MATCHTYPE_REGEXPCASE:
- case MATCHTYPE_REGEXP:
- if (!prop->preg && (prop->error == 0)) {
- prop->preg = g_new0(regex_t, 1);
- /* if regexp then don't use the escaped string */
- if (regcomp(prop->preg, prop->expr,
- REG_NOSUB | REG_EXTENDED
- | ((prop->matchtype == MATCHTYPE_REGEXPCASE)
- ? REG_ICASE : 0)) != 0) {
- prop->error = 1;
- g_free(prop->preg);
- prop->preg = NULL;
- }
- }
- if (prop->preg == NULL)
- return FALSE;
-
- if (regexec(prop->preg, str, 0, NULL, 0) == 0)
- return TRUE;
- else
- return FALSE;
-
- case MATCHTYPE_MATCH:
- return (strstr(str, prop->expr) != NULL);
- /* FIXME: put upper in unesc_str */
- case MATCHTYPE_MATCHCASE:
- str2 = alloca(strlen(prop->expr) + 1);
- strcpy(str2, prop->expr);
- g_strup(str2);
- str1 = alloca(strlen(str) + 1);
- strcpy(str1, str);
- g_strup(str1);
- return (strstr(str1, str2) != NULL);
-
- default:
- return FALSE;
- }
- }
- /* FIXME body search is a hack. */
- static gboolean matcherprop_string_decode_match(MatcherProp *prop, const gchar *str)
- {
- gchar *utf = NULL;
- gchar tmp[BUFFSIZE];
- gboolean res = FALSE;
- if (str == NULL)
- return FALSE;
- /* we try to decode QP first, because it's faster than base64 */
- qp_decode_const(tmp, BUFFSIZE-1, str);
- if (!g_utf8_validate(tmp, -1, NULL)) {
- utf = conv_codeset_strdup
- (tmp, conv_get_locale_charset_str_no_utf8(),
- CS_INTERNAL);
- res = matcherprop_string_match(prop, utf);
- g_free(utf);
- } else {
- res = matcherprop_string_match(prop, tmp);
- }
-
- if (res == FALSE && (strchr(prop->expr, '=') || strchr(prop->expr, '_')
- || strchr(str, '=') || strchr(str, '_'))) {
- /* if searching for something with an equal char, maybe
- * we should try to match the non-decoded string.
- * In case it was not qp-encoded. */
- if (!g_utf8_validate(str, -1, NULL)) {
- utf = conv_codeset_strdup
- (str, conv_get_locale_charset_str_no_utf8(),
- CS_INTERNAL);
- res = matcherprop_string_match(prop, utf);
- g_free(utf);
- } else {
- res = matcherprop_string_match(prop, str);
- }
- }
- /* FIXME base64 decoding is too slow, especially since text can
- * easily be handled as base64. Don't even try now. */
- return res;
- }
- #ifdef USE_PTHREAD
- typedef struct _thread_data {
- const gchar *cmd;
- gboolean done;
- } thread_data;
- #endif
- #ifdef USE_PTHREAD
- void *matcher_test_thread(void *data)
- {
- thread_data *td = (thread_data *)data;
- int result = -1;
- pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
- pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
- result = system(td->cmd);
- if (result) perror("system");
- td->done = TRUE; /* let the caller thread join() */
- return GINT_TO_POINTER(result);
- }
- #endif
- /*!
- *\brief Execute a command defined in the matcher structure
- *
- *\param prop Pointer to matcher structure
- *\param info Pointer to message info structure
- *
- *\return gboolean TRUE if command was executed succesfully
- */
- static gboolean matcherprop_match_test(const MatcherProp *prop,
- MsgInfo *info)
- {
- gchar *file;
- gchar *cmd;
- gint retval;
- #ifdef USE_PTHREAD
- pthread_t pt;
- thread_data *td = g_new0(thread_data, 1);
- void *res = NULL;
- time_t start_time = time(NULL);
- #endif
- file = procmsg_get_message_file(info);
- if (file == NULL)
- return FALSE;
- g_free(file);
- cmd = matching_build_command(prop->expr, info);
- if (cmd == NULL)
- return FALSE;
- #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
- td->cmd = cmd;
- td->done = FALSE;
- if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
- matcher_test_thread, td) != 0)
- retval = system(cmd);
- else {
- printf("waiting for test thread\n");
- while(!td->done) {
- /* don't let the interface freeze while waiting */
- claws_do_idle();
- if (time(NULL) - start_time > 30) {
- pthread_cancel(pt);
- td->done = TRUE;
- retval = -1;
- }
- }
- pthread_join(pt, &res);
- retval = GPOINTER_TO_INT(res);
- printf(" test thread returned %d\n", retval);
- }
- g_free(td);
- #else
- retval = system(cmd);
- #endif
- debug_print("Command exit code: %d\n", retval);
- g_free(cmd);
- return (retval == 0);
- }
- /*!
- *\brief Check if a message matches the condition in a matcher
- * structure.
- *
- *\param prop Pointer to matcher structure
- *\param info Pointer to message info
- *
- *\return gboolean TRUE if a match
- */
- gboolean matcherprop_match(MatcherProp *prop,
- MsgInfo *info)
- {
- time_t t;
- switch(prop->criteria) {
- case MATCHCRITERIA_ALL:
- return 1;
- case MATCHCRITERIA_UNREAD:
- return MSG_IS_UNREAD(info->flags);
- case MATCHCRITERIA_NOT_UNREAD:
- return !MSG_IS_UNREAD(info->flags);
- case MATCHCRITERIA_NEW:
- return MSG_IS_NEW(info->flags);
- case MATCHCRITERIA_NOT_NEW:
- return !MSG_IS_NEW(info->flags);
- case MATCHCRITERIA_MARKED:
- return MSG_IS_MARKED(info->flags);
- case MATCHCRITERIA_NOT_MARKED:
- return !MSG_IS_MARKED(info->flags);
- case MATCHCRITERIA_DELETED:
- return MSG_IS_DELETED(info->flags);
- case MATCHCRITERIA_NOT_DELETED:
- return !MSG_IS_DELETED(info->flags);
- case MATCHCRITERIA_REPLIED:
- return MSG_IS_REPLIED(info->flags);
- case MATCHCRITERIA_NOT_REPLIED:
- return !MSG_IS_REPLIED(info->flags);
- case MATCHCRITERIA_FORWARDED:
- return MSG_IS_FORWARDED(info->flags);
- case MATCHCRITERIA_NOT_FORWARDED:
- return !MSG_IS_FORWARDED(info->flags);
- case MATCHCRITERIA_LOCKED:
- return MSG_IS_LOCKED(info->flags);
- case MATCHCRITERIA_NOT_LOCKED:
- return !MSG_IS_LOCKED(info->flags);
- case MATCHCRITERIA_COLORLABEL:
- return MSG_GET_COLORLABEL_VALUE(info->flags) == prop->value;
- case MATCHCRITERIA_NOT_COLORLABEL:
- return MSG_GET_COLORLABEL_VALUE(info->flags) != prop->value;
- case MATCHCRITERIA_IGNORE_THREAD:
- return MSG_IS_IGNORE_THREAD(info->flags);
- case MATCHCRITERIA_NOT_IGNORE_THREAD:
- return !MSG_IS_IGNORE_THREAD(info->flags);
- case MATCHCRITERIA_SUBJECT:
- return matcherprop_string_match(prop, info->subject);
- case MATCHCRITERIA_NOT_SUBJECT:
- return !matcherprop_string_match(prop, info->subject);
- case MATCHCRITERIA_FROM:
- return matcherprop_string_match(prop, info->from);
- case MATCHCRITERIA_NOT_FROM:
- return !matcherprop_string_match(prop, info->from);
- case MATCHCRITERIA_TO:
- return matcherprop_string_match(prop, info->to);
- case MATCHCRITERIA_NOT_TO:
- return !matcherprop_string_match(prop, info->to);
- case MATCHCRITERIA_CC:
- return matcherprop_string_match(prop, info->cc);
- case MATCHCRITERIA_NOT_CC:
- return !matcherprop_string_match(prop, info->cc);
- case MATCHCRITERIA_TO_OR_CC:
- return matcherprop_string_match(prop, info->to)
- || matcherprop_string_match(prop, info->cc);
- case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
- return !(matcherprop_string_match(prop, info->to)
- || matcherprop_string_match(prop, info->cc));
- case MATCHCRITERIA_AGE_GREATER:
- t = time(NULL);
- return ((t - info->date_t) / (60 * 60 * 24)) > prop->value;
- case MATCHCRITERIA_AGE_LOWER:
- t = time(NULL);
- return ((t - info->date_t) / (60 * 60 * 24)) < prop->value;
- case MATCHCRITERIA_SCORE_GREATER:
- return info->score > prop->value;
- case MATCHCRITERIA_SCORE_LOWER:
- return info->score < prop->value;
- case MATCHCRITERIA_SCORE_EQUAL:
- return info->score == prop->value;
- case MATCHCRITERIA_SIZE_GREATER:
- /* FIXME: info->size is an off_t */
- return info->size > (off_t) prop->value;
- case MATCHCRITERIA_SIZE_EQUAL:
- /* FIXME: info->size is an off_t */
- return info->size == (off_t) prop->value;
- case MATCHCRITERIA_SIZE_SMALLER:
- /* FIXME: info->size is an off_t */
- return info->size < (off_t) prop->value;
- case MATCHCRITERIA_PARTIAL:
- /* FIXME: info->size is an off_t */
- return (info->total_size != 0 && info->size != (off_t)info->total_size);
- case MATCHCRITERIA_NOT_PARTIAL:
- /* FIXME: info->size is an off_t */
- return (info->total_size == 0 || info->size == (off_t)info->total_size);
- case MATCHCRITERIA_NEWSGROUPS:
- return matcherprop_string_match(prop, info->newsgroups);
- case MATCHCRITERIA_NOT_NEWSGROUPS:
- return !matcherprop_string_match(prop, info->newsgroups);
- case MATCHCRITERIA_INREPLYTO:
- return matcherprop_string_match(prop, info->inreplyto);
- case MATCHCRITERIA_NOT_INREPLYTO:
- return !matcherprop_string_match(prop, info->inreplyto);
- /* FIXME: Using inreplyto, but matching the (newly implemented)
- * list of references is better */
- case MATCHCRITERIA_REFERENCES:
- return matcherprop_string_match(prop, info->inreplyto);
- case MATCHCRITERIA_NOT_REFERENCES:
- return !matcherprop_string_match(prop, info->inreplyto);
- case MATCHCRITERIA_TEST:
- return matcherprop_match_test(prop, info);
- case MATCHCRITERIA_NOT_TEST:
- return !matcherprop_match_test(prop, info);
- default:
- return FALSE;
- }
- }
- /* ********************* MatcherList *************************** */
- /*!
- *\brief Create a new list of matchers
- *
- *\param matchers List of matcher structures
- *\param bool_and Operator
- *
- *\return MatcherList * New list
- */
- MatcherList *matcherlist_new(GSList *matchers, gboolean bool_and)
- {
- MatcherList *cond;
- cond = g_new0(MatcherList, 1);
- cond->matchers = matchers;
- cond->bool_and = bool_and;
- return cond;
- }
- /*!
- *\brief Frees a list of matchers
- *
- *\param cond List of matchers
- */
- void matcherlist_free(MatcherList *cond)
- {
- GSList *l;
- g_return_if_fail(cond);
- for (l = cond->matchers ; l != NULL ; l = g_slist_next(l)) {
- matcherprop_free((MatcherProp *) l->data);
- }
- g_free(cond);
- }
- /*!
- *\brief Skip all headers in a message file
- *
- *\param fp Message file
- */
- static void matcherlist_skip_headers(FILE *fp)
- {
- gchar buf[BUFFSIZE];
- while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1)
- ;
- }
- /*!
- *\brief Check if a header matches a matcher condition
- *
- *\param matcher Matcher structure to check header for
- *\param buf Header name
- *
- *\return boolean TRUE if matching header
- */
- static gboolean matcherprop_match_one_header(MatcherProp *matcher,
- gchar *buf)
- {
- gboolean result = FALSE;
- Header *header = NULL;
- switch (matcher->criteria) {
- case MATCHCRITERIA_HEADER:
- case MATCHCRITERIA_NOT_HEADER:
- header = procheader_parse_header(buf);
- if (!header)
- return FALSE;
- if (procheader_headername_equal(header->name,
- matcher->header)) {
- if (matcher->criteria == MATCHCRITERIA_HEADER)
- result = matcherprop_string_match(matcher, header->body);
- else
- result = !matcherprop_string_match(matcher, header->body);
- procheader_header_free(header);
- return result;
- }
- else {
- procheader_header_free(header);
- }
- break;
- case MATCHCRITERIA_HEADERS_PART:
- return matcherprop_string_match(matcher, buf);
- case MATCHCRITERIA_MESSAGE:
- return matcherprop_string_decode_match(matcher, buf);
- case MATCHCRITERIA_NOT_MESSAGE:
- return !matcherprop_string_decode_match(matcher, buf);
- case MATCHCRITERIA_NOT_HEADERS_PART:
- return !matcherprop_string_match(matcher, buf);
- case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
- case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
- {
- GSList *address_list = NULL;
- gint match = MATCH_ONE;
- gboolean found = FALSE;
- /* how many address headers are me trying to mach? */
- if (strcasecmp(matcher->header, _("Any")) == 0)
- match = MATCH_ANY;
- else if (strcasecmp(matcher->header, Q_("Filtering Matcher Menu|All")) == 0)
- match = MATCH_ALL;
- if (match == MATCH_ONE) {
- /* matching one address header exactly, is that the right one? */
- header = procheader_parse_header(buf);
- if (!header ||
- !procheader_headername_equal(header->name, matcher->header))
- return FALSE;
- address_list = address_list_append(address_list, header->body);
- if (address_list == NULL)
- return FALSE;
- } else {
- header = procheader_parse_header(buf);
- if (!header)
- return FALSE;
- /* address header is one of the headers we have to match when checking
- for any address header or all address headers? */
- if (procheader_headername_equal(header->name, "From") ||
- procheader_headername_equal(header->name, "To") ||
- procheader_headername_equal(header->name, "Cc") ||
- procheader_headername_equal(header->name, "Reply-To") ||
- procheader_headername_equal(header->name, "Sender"))
- address_list = address_list_append(address_list, header->body);
- if (address_list == NULL)
- return FALSE;
- }
- found = match_with_addresses_in_addressbook
- (matcher, address_list, matcher->criteria,
- matcher->expr, match);
- g_slist_free(address_list);
- if (matcher->criteria == MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK)
- return !found;
- else
- return found;
- }
- }
- return FALSE;
- }
- /*!
- *\brief Check if the matcher structure wants headers to
- * be matched
- *
- *\param matcher Matcher structure
- *
- *\return gboolean TRUE if the matcher structure describes
- * a header match condition
- */
- static gboolean matcherprop_criteria_headers(const MatcherProp *matcher)
- {
- switch (matcher->criteria) {
- case MATCHCRITERIA_HEADER:
- case MATCHCRITERIA_NOT_HEADER:
- case MATCHCRITERIA_HEADERS_PART:
- case MATCHCRITERIA_NOT_HEADERS_PART:
- case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
- case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
- return TRUE;
- default:
- return FALSE;
- }
- }
- /*!
- *\brief Check if the matcher structure wants the message
- * to be matched (just perform an action on any
- * message)
- *
- *\param matcher Matcher structure
- *
- *\return gboolean TRUE if matcher condition should match
- * a message
- */
- static gboolean matcherprop_criteria_message(MatcherProp *matcher)
- {
- switch (matcher->criteria) {
- case MATCHCRITERIA_MESSAGE:
- case MATCHCRITERIA_NOT_MESSAGE:
- return TRUE;
- default:
- return FALSE;
- }
- }
- /*!
- *\brief Check if a list of conditions matches one header in
- * a message file.
- *
- *\param matchers List of conditions
- *\param fp Message file
- *
- *\return gboolean TRUE if one of the headers is matched by
- * the list of conditions.
- */
- static gboolean matcherlist_match_headers(MatcherList *matchers, FILE *fp)
- {
- GSList *l;
- gchar buf[BUFFSIZE];
- while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) {
- for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
- MatcherProp *matcher = (MatcherProp *) l->data;
- gint match = MATCH_ANY;
- if (matcher->done)
- continue;
- /* determine the match range (all, any are our concern here) */
- if (matcher->criteria == MATCHCRITERIA_NOT_HEADERS_PART ||
- matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
- match = MATCH_ALL;
- } else if (matcher->criteria == MATCHCRITERIA_FOUND_IN_ADDRESSBOOK ||
- matcher->criteria == MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK) {
- Header *header = NULL;
- /* address header is one of the headers we have to match when checking
- for any address header or all address headers? */
- header = procheader_parse_header(buf);
- if (header &&
- (procheader_headername_equal(header->name, "From") ||
- procheader_headername_equal(header->name, "To") ||
- procheader_headername_equal(header->name, "Cc") ||
- procheader_headername_equal(header->name, "Reply-To") ||
- procheader_headername_equal(header->name, "Sender"))) {
- if (strcasecmp(matcher->header, _("Any")) == 0)
- match = MATCH_ANY;
- else if (strcasecmp(matcher->header, Q_("Filtering Matcher Menu|All")) == 0)
- match = MATCH_ALL;
- else
- match = MATCH_ONE;
- } else {
- /* further call to matcherprop_match_one_header() can't match
- and it irrelevant, so: don't alter the match result */
- continue;
- }
- }
- /* ZERO line must NOT match for the rule to match.
- */
- if (match == MATCH_ALL) {
- if (matcherprop_match_one_header(matcher, buf)) {
- matcher->result = TRUE;
- } else {
- matcher->result = FALSE;
- matcher->done = TRUE;
- }
- /* else, just one line matching is enough for the rule to match
- */
- } else if (matcherprop_criteria_headers(matcher) ||
- matcherprop_criteria_message(matcher)) {
- if (matcherprop_match_one_header(matcher, buf)) {
- matcher->result = TRUE;
- matcher->done = TRUE;
- }
- }
-
- /* if the rule matched and the matchers are OR, no need to
- * check the others */
- if (matcher->result && matcher->done) {
- if (!matchers->bool_and)
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- /*!
- *\brief Check if a matcher wants to check the message body
- *
- *\param matcher Matcher structure
- *
- *\return gboolean TRUE if body must be matched.
- */
- static gboolean matcherprop_criteria_body(const MatcherProp *matcher)
- {
- switch (matcher->criteria) {
- case MATCHCRITERIA_BODY_PART:
- case MATCHCRITERIA_NOT_BODY_PART:
- return TRUE;
- default:
- return FALSE;
- }
- }
- /*!
- *\brief Check if a (line) string matches the criteria
- * described by a matcher structure
- *
- *\param matcher Matcher structure
- *\param line String
- *
- *\return gboolean TRUE if string matches criteria
- */
- static gboolean matcherprop_match_line(MatcherProp *matcher, const gchar *line)
- {
- switch (matcher->criteria) {
- case MATCHCRITERIA_BODY_PART:
- case MATCHCRITERIA_MESSAGE:
- return matcherprop_string_decode_match(matcher, line);
- case MATCHCRITERIA_NOT_BODY_PART:
- case MATCHCRITERIA_NOT_MESSAGE:
- return !matcherprop_string_decode_match(matcher, line);
- }
- return FALSE;
- }
- /*!
- *\brief Check if a line in a message file's body matches
- * the criteria
- *
- *\param matchers List of conditions
- *\param fp Message file
- *
- *\return gboolean TRUE if succesful match
- */
- static gboolean matcherlist_match_body(MatcherList *matchers, FILE *fp)
- {
- GSList *l;
- gchar buf[BUFFSIZE];
-
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
- MatcherProp *matcher = (MatcherProp *) l->data;
-
- if (matcher->done)
- continue;
- /* if the criteria is ~body_part or ~message, ZERO lines
- * must NOT match for the rule to match. */
- if (matcher->criteria == MATCHCRITERIA_NOT_BODY_PART ||
- matcher->criteria == MATCHCRITERIA_NOT_MESSAGE) {
- if (matcherprop_match_line(matcher, buf)) {
- matcher->result = TRUE;
- } else {
- matcher->result = FALSE;
- matcher->done = TRUE;
- }
- /* else, just one line has to match */
- } else if (matcherprop_criteria_body(matcher) ||
- matcherprop_criteria_message(matcher)) {
- if (matcherprop_match_line(matcher, buf)) {
- matcher->result = TRUE;
- matcher->done = TRUE;
- }
- }
- /* if the matchers are OR'ed and the rule matched,
- * no need to check the others. */
- if (matcher->result && matcher->done) {
- if (!matchers->bool_and)
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- /*!
- *\brief Check if a message file matches criteria
- *
- *\param matchers Criteria
- *\param info Message info
- *\param result Default result
- *
- *\return gboolean TRUE if matched
- */
- gboolean matcherlist_match_file(MatcherList *matchers, MsgInfo *info,
- gboolean result)
- {
- gboolean read_headers;
- gboolean read_body;
- GSList *l;
- FILE *fp;
- gchar *file;
- /* file need to be read ? */
- read_headers = FALSE;
- read_body = FALSE;
- for (l = matchers->matchers ; l != NULL ; l = g_slist_next(l)) {
- MatcherProp *matcher = (MatcherProp *) l->data;
- if (matcherprop_criteria_headers(matcher))
- read_headers = TRUE;
- if (matcherprop_criteria_body(matcher))
- read_body = TRUE;
- if (matcherprop_criteria_message(matcher)) {
- read_headers = TRUE;
- read_body = TRUE;
- }
- matcher->result = FALSE;
- matcher->done = FALSE;
- }
- if (!read_headers && !read_body)
- return result;
- file = procmsg_get_message_file_full(info, read_headers, read_body);
- if (file == NULL)
- return FALSE;
- if ((fp = g_fopen(file, "rb")) == NULL) {
- FILE_OP_ERROR(file, "fopen");
- g_free(file);
- return result;
- }
- /* read the headers */
- if (read_headers) {
- if (matcherlist_match_headers(matchers, fp))
- read_body = FALSE;
- } else {
- matcherlist_skip_headers(fp);
- }
- /* read the body */
- if (read_body) {
- matcherlist_match_body(matchers, fp);
- }
-
- for (l = matchers->matchers; l != NULL; l = g_slist_next(l)) {
- MatcherProp *matcher = (MatcherProp *) l->data;
- if (matcherprop_criteria_headers(matcher) ||
- matcherprop_criteria_body(matcher) ||
- matcherprop_criteria_message(matcher)) {
- if (matcher->result) {
- if (!matchers->bool_and) {
- result = TRUE;
- break;
- }
- }
- else {
- if (matchers->bool_and) {
- result = FALSE;
- break;
- }
- }
- }
- }
- g_free(file);
- fclose(fp);
-
- return result;
- }
- /*!
- *\brief Test list of conditions on a message.
- *
- *\param matchers List of conditions
- *\param info Message info
- *
- *\return gboolean TRUE if matched
- */
- gboolean matcherlist_match(MatcherList *matchers, MsgInfo *info)
- {
- GSList *l;
- gboolean result;
- if (!matchers)
- return FALSE;
- if (matchers->bool_and)
- result = TRUE;
- else
- result = FALSE;
- /* test the cached elements */
- for (l = matchers->matchers; l != NULL ;l = g_slist_next(l)) {
- MatcherProp *matcher = (MatcherProp *) l->data;
- switch(matcher->criteria) {
- case MATCHCRITERIA_ALL:
- case MATCHCRITERIA_UNREAD:
- case MATCHCRITERIA_NOT_UNREAD:
- case MATCHCRITERIA_NEW:
- case MATCHCRITERIA_NOT_NEW:
- case MATCHCRITERIA_MARKED:
- case MATCHCRITERIA_NOT_MARKED:
- case MATCHCRITERIA_DELETED:
- case MATCHCRITERIA_NOT_DELETED:
- case MATCHCRITERIA_REPLIED:
- case MATCHCRITERIA_NOT_REPLIED:
- case MATCHCRITERIA_FORWARDED:
- case MATCHCRITERIA_NOT_FORWARDED:
- case MATCHCRITERIA_LOCKED:
- case MATCHCRITERIA_NOT_LOCKED:
- case MATCHCRITERIA_COLORLABEL:
- case MATCHCRITERIA_NOT_COLORLABEL:
- case MATCHCRITERIA_IGNORE_THREAD:
- case MATCHCRITERIA_NOT_IGNORE_THREAD:
- case MATCHCRITERIA_SUBJECT:
- case MATCHCRITERIA_NOT_SUBJECT:
- case MATCHCRITERIA_FROM:
- case MATCHCRITERIA_NOT_FROM:
- case MATCHCRITERIA_TO:
- case MATCHCRITERIA_NOT_TO:
- case MATCHCRITERIA_CC:
- case MATCHCRITERIA_NOT_CC:
- case MATCHCRITERIA_TO_OR_CC:
- case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
- case MATCHCRITERIA_AGE_GREATER:
- case MATCHCRITERIA_AGE_LOWER:
- case MATCHCRITERIA_NEWSGROUPS:
- case MATCHCRITERIA_NOT_NEWSGROUPS:
- case MATCHCRITERIA_INREPLYTO:
- case MATCHCRITERIA_NOT_INREPLYTO:
- case MATCHCRITERIA_REFERENCES:
- case MATCHCRITERIA_NOT_REFERENCES:
- case MATCHCRITERIA_SCORE_GREATER:
- case MATCHCRITERIA_SCORE_LOWER:
- case MATCHCRITERIA_SCORE_EQUAL:
- case MATCHCRITERIA_SIZE_GREATER:
- case MATCHCRITERIA_SIZE_SMALLER:
- case MATCHCRITERIA_SIZE_EQUAL:
- case MATCHCRITERIA_TEST:
- case MATCHCRITERIA_NOT_TEST:
- case MATCHCRITERIA_PARTIAL:
- case MATCHCRITERIA_NOT_PARTIAL:
- if (matcherprop_match(matcher, info)) {
- if (!matchers->bool_and) {
- return TRUE;
- }
- }
- else {
- if (matchers->bool_and) {
- return FALSE;
- }
- }
- }
- }
- /* test the condition on the file */
- if (matcherlist_match_file(matchers, info, result)) {
- if (!matchers->bool_and)
- return TRUE;
- }
- else {
- if (matchers->bool_and)
- return FALSE;
- }
- return result;
- }
- static gint quote_filter_str(gchar * result, guint size,
- const gchar * path)
- {
- const gchar * p;
- gchar * result_p;
- guint remaining;
- result_p = result;
- remaining = size;
- for(p = path ; * p != '\0' ; p ++) {
- if ((* p != '\"') && (* p != '\\')) {
- if (remaining > 0) {
- * result_p = * p;
- result_p ++;
- remaining --;
- }
- else {
- result[size - 1] = '\0';
- return -1;
- }
- }
- else {
- if (remaining >= 2) {
- * result_p = '\\';
- result_p ++;
- * result_p = * p;
- result_p ++;
- remaining -= 2;
- }
- else {
- result[size - 1] = '\0';
- return -1;
- }
- }
- }
- if (remaining > 0) {
- * result_p = '\0';
- }
- else {
- result[size - 1] = '\0';
- return -1;
- }
-
- return 0;
- }
- gchar * matcher_quote_str(const gchar * src)
- {
- gchar * res;
- gint len;
-
- len = strlen(src) * 2 + 1;
- res = g_malloc(len);
- quote_filter_str(res, len, src);
-
- return res;
- }
- /*!
- *\brief Convert a matcher structure to a string
- *
- *\param matcher Matcher structure
- *
- *\return gchar * Newly allocated string
- */
- gchar *matcherprop_to_string(MatcherProp *matcher)
- {
- gchar *matcher_str = NULL;
- const gchar *criteria_str;
- const gchar *matchtype_str;
- int i;
- gchar * quoted_expr;
- gchar * quoted_header;
-
- criteria_str = NULL;
- for (i = 0; i < (int) (sizeof(matchparser_tab) / sizeof(MatchParser)); i++) {
- if (matchparser_tab[i].id == matcher->criteria)
- criteria_str = matchparser_tab[i].str;
- }
- if (criteria_str == NULL)
- return NULL;
- switch (matcher->criteria) {
- case MATCHCRITERIA_AGE_GREATER:
- case MATCHCRITERIA_AGE_LOWER:
- case MATCHCRITERIA_SCORE_GREATER:
- case MATCHCRITERIA_SCORE_LOWER:
- case MATCHCRITERIA_SCORE_EQUAL:
- case MATCHCRITERIA_SIZE_GREATER:
- case MATCHCRITERIA_SIZE_SMALLER:
- case MATCHCRITERIA_SIZE_EQUAL:
- case MATCHCRITERIA_COLORLABEL:
- case MATCHCRITERIA_NOT_COLORLABEL:
- return g_strdup_printf("%s %i", criteria_str, matcher->value);
- case MATCHCRITERIA_ALL:
- case MATCHCRITERIA_UNREAD:
- case MATCHCRITERIA_NOT_UNREAD:
- case MATCHCRITERIA_NEW:
- case MATCHCRITERIA_NOT_NEW:
- case MATCHCRITERIA_MARKED:
- case MATCHCRITERIA_NOT_MARKED:
- case MATCHCRITERIA_DELETED:
- case MATCHCRITERIA_NOT_DELETED:
- case MATCHCRITERIA_REPLIED:
- case MATCHCRITERIA_NOT_REPLIED:
- case MATCHCRITERIA_FORWARDED:
- case MATCHCRITERIA_NOT_FORWARDED:
- case MATCHCRITERIA_LOCKED:
- case MATCHCRITERIA_NOT_LOCKED:
- case MATCHCRITERIA_PARTIAL:
- case MATCHCRITERIA_NOT_PARTIAL:
- case MATCHCRITERIA_IGNORE_THREAD:
- case MATCHCRITERIA_NOT_IGNORE_THREAD:
- return g_strdup(criteria_str);
- case MATCHCRITERIA_TEST:
- case MATCHCRITERIA_NOT_TEST:
- quoted_expr = matcher_quote_str(matcher->expr);
- matcher_str = g_strdup_printf("%s \"%s\"",
- criteria_str, quoted_expr);
- g_free(quoted_expr);
- return matcher_str;
- case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
- case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
- quoted_header = matcher_quote_str(matcher->header);
- quoted_expr = matcher_quote_str(matcher->expr);
- matcher_str = g_strdup_printf("%s \"%s\" in \"%s\"",
- criteria_str, quoted_header, quoted_expr);
- g_free(quoted_header);
- g_free(quoted_expr);
- return matcher_str;
- }
- matchtype_str = NULL;
- for (i = 0; i < sizeof matchparser_tab / sizeof matchparser_tab[0]; i++) {
- if (matchparser_tab[i].id == matcher->matchtype)
- matchtype_str = matchparser_tab[i].str;
- }
- if (matchtype_str == NULL)
- return NULL;
- switch (matcher->matchtype) {
- case MATCHTYPE_MATCH:
- case MATCHTYPE_MATCHCASE:
- case MATCHTYPE_REGEXP:
- case MATCHTYPE_REGEXPCASE:
- quoted_expr = matcher_quote_str(matcher->expr);
- if (matcher->header) {
- quoted_header = matcher_quote_str(matcher->header);
- matcher_str = g_strdup_printf
- ("%s \"%s\" %s \"%s\"",
- criteria_str, quoted_header,
- matchtype_str, quoted_expr);
- g_free(quoted_header);
- }
- else
- matcher_str = g_strdup_printf
- ("%s %s \"%s\"", criteria_str,
- matchtype_str, quoted_expr);
- g_free(quoted_expr);
- break;
- }
- return matcher_str;
- }
- /*!
- *\brief Convert a list of conditions to a string
- *
- *\param matchers List of conditions
- *
- *\return gchar * Newly allocated string
- */
- gchar *matcherlist_to_string(const MatcherList *matchers)
- {
- gint count;
- gchar **vstr;
- GSList *l;
- gchar **cur_str;
- gchar *result = NULL;
- count = g_slist_length(matchers->matchers);
- vstr = g_new(gchar *, count + 1);
- for (l = matchers->matchers, cur_str = vstr; l != NULL;
- l = g_slist_next(l), cur_str ++) {
- *cur_str = matcherprop_to_string((MatcherProp *) l->data);
- if (*cur_str == NULL)
- break;
- }
- *cur_str = NULL;
-
- if (matchers->bool_and)
- result = g_strjoinv(" & ", vstr);
- else
- result = g_strjoinv(" | ", vstr);
- for (cur_str = vstr ; *cur_str != NULL ; cur_str ++)
- g_free(*cur_str);
- g_free(vstr);
- return result;
- }
- #define STRLEN_ZERO(s) ((s) ? strlen(s) : 0)
- #define STRLEN_DEFAULT(s,d) ((s) ? strlen(s) : STRLEN_ZERO(d))
- static void add_str_default(gchar ** dest,
- const gchar * s, const gchar * d)
- {
- gchar quoted_str[4096];
- const gchar * str;
-
- if (s != NULL)
- str = s;
- else
- str = d;
-
- quote_cmd_argument(quoted_str, sizeof(quoted_str), str);
- strcpy(* dest, quoted_str);
-
- (* dest) += strlen(* dest);
- }
- /* matching_build_command() - preferably cmd should be unescaped */
- /*!
- *\brief Build the command line to execute
- *
- *\param cmd String with command line specifiers
- *\param info Message info to use for command
- *
- *\return gchar * Newly allocated string
- */
- gchar *matching_build_command(const gchar *cmd, MsgInfo *info)
- {
- const gchar *s = cmd;
- gchar *filename = NULL;
- gchar *processed_cmd;
- gchar *p;
- gint size;
- const gchar *const no_subject = _("(none)") ;
- const gchar *const no_from = _("(none)") ;
- const gchar *const no_to = _("(none)") ;
- const gchar *const no_cc = _("(none)") ;
- const gchar *const no_date = _("(none)") ;
- const gchar *const no_msgid = _("(none)") ;
- const gchar *const no_newsgroups = _("(none)") ;
- const gchar *const no_references = _("(none)") ;
- size = STRLEN_ZERO(cmd) + 1;
- while (*s != '\0') {
- if (*s == '%') {
- s++;
- switch (*s) {
- case '%':
- size -= 1;
- break;
- case 's': /* subject */
- size += STRLEN_DEFAULT(info->subject, no_subject) - 2;
- break;
- case 'f': /* from */
- size += STRLEN_DEFAULT(info->from, no_from) - 2;
- break;
- case 't': /* to */
- size += STRLEN_DEFAULT(info->to, no_to) - 2;
- break;
- case 'c': /* cc */
- size += STRLEN_DEFAULT(info->cc, no_cc) - 2;
- break;
- case 'd': /* date */
- size += STRLEN_DEFAULT(info->date, no_date) - 2;
- break;
- case 'i': /* message-id */
- size += STRLEN_DEFAULT(info->msgid, no_msgid) - 2;
- break;
- case 'n': /* newsgroups */
- size += STRLEN_DEFAULT(info->newsgroups, no_newsgroups) - 2;
- break;
- case 'r': /* references */
- /* FIXME: using the inreplyto header for reference */
- size += STRLEN_DEFAULT(info->inreplyto, no_references) - 2;
- break;
- case 'F': /* file */
- if (filename == NULL)
- filename = folder_item_fetch_msg(info->folder, info->msgnum);
-
- if (filename == NULL) {
- g_warning("filename is not set");
- return NULL;
- }
- else {
- size += strlen(filename) - 2;
- }
- break;
- }
- s++;
- }
- else s++;
- }
-
- /* as the string can be quoted, we double the result */
- size *= 2;
- processed_cmd = g_new0(gchar, size);
- s = cmd;
- p = processed_cmd;
- while (*s != '\0') {
- if (*s == '%') {
- s++;
- switch (*s) {
- case '%':
- *p = '%';
- p++;
- break;
- case 's': /* subject */
- add_str_default(&p, info->subject,
- no_subject);
- break;
- case 'f': /* from */
- add_str_default(&p, info->from,
- no_from);
- break;
- case 't': /* to */
- add_str_default(&p, info->to,
- no_to);
- break;
- case 'c': /* cc */
- add_str_default(&p, info->cc,
- no_cc);
- break;
- case 'd': /* date */
- add_str_default(&p, info->date,
- no_date);
- break;
- case 'i': /* message-id */
- add_str_default(&p, info->msgid,
- no_msgid);
- break;
- case 'n': /* newsgroups */
- add_str_default(&p, info->newsgroups,
- no_newsgroups);
- break;
- case 'r': /* references */
- /* FIXME: using the inreplyto header for references */
- add_str_default(&p, info->inreplyto, no_references);
- break;
- case 'F': /* file */
- if (filename != NULL)
- add_str_default(&p, filename, NULL);
- break;
- default:
- *p = '%';
- p++;
- *p = *s;
- p++;
- break;
- }
- s++;
- }
- else {
- *p = *s;
- p++;
- s++;
- }
- }
- g_free(filename);
-
- return processed_cmd;
- }
- #undef STRLEN_DEFAULT
- #undef STRLEN_ZERO
- /* ************************************************************ */
- /*!
- *\brief Write filtering list to file
- *
- *\param fp File
- *\param prefs_filtering List of filtering conditions
- */
- static void prefs_filtering_write(FILE *fp, GSList *prefs_filtering)
- {
- GSList *cur = NULL;
- for (cur = prefs_filtering; cur != NULL; cur = cur->next) {
- gchar *filtering_str = NULL;
- gchar *tmp_name = NULL;
- FilteringProp *prop = NULL;
- if (NULL == (prop = (FilteringProp *) cur->data))
- continue;
-
- if (NULL == (filtering_str = filteringprop_to_string(prop)))
- continue;
- if (prop->enabled) {
- if (fputs("enabled ", fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs");
- return;
- }
- } else {
- if (fputs("disabled ", fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs");
- return;
- }
- }
- if (fputs("rulename \"", fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs");
- g_free(filtering_str);
- return;
- }
- tmp_name = prop->name;
- while (tmp_name && *tmp_name != '\0') {
- if (*tmp_name != '"') {
- if (fputc(*tmp_name, fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs || fputc");
- g_free(filtering_str);
- return;
- }
- } else if (*tmp_name == '"') {
- if (fputc('\\', fp) == EOF ||
- fputc('"', fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs || fputc");
- g_free(filtering_str);
- return;
- }
- }
- tmp_name ++;
- }
- if (fputs("\" ", fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs");
- g_free(filtering_str);
- return;
- }
- if (prop->account_id != 0) {
- gchar *tmp = NULL;
- tmp = g_strdup_printf("account %d ", prop->account_id);
- if (fputs(tmp, fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs");
- g_free(tmp);
- return;
- }
- g_free(tmp);
- }
- if(fputs(filtering_str, fp) == EOF ||
- fputc('\n', fp) == EOF) {
- FILE_OP_ERROR("filtering config", "fputs || fputc");
- g_free(filtering_str);
- return;
- }
- g_free(filtering_str);
- }
- }
- /*!
- *\brief Write matchers from a folder item
- *
- *\param node Node with folder info
- *\param data File pointer
- *
- *\return gboolean FALSE
- */
- static gboolean prefs_matcher_write_func(GNode *node, gpointer data)
- {
- FolderItem *item;
- FILE *fp = data;
- gchar *id;
- GSList *prefs_filtering;
- item = node->data;
- /* prevent warning */
- if (item->path == NULL)
- return FALSE;
- id = folder_item_get_identifier(item);
- if (id == NULL)
- return FALSE;
- prefs_filtering = item->prefs->processing;
- if (prefs_filtering != NULL) {
- fprintf(fp, "[%s]\n", id);
- prefs_filtering_write(fp, prefs_filtering);
- fputc('\n', fp);
- }
- g_free(id);
- return FALSE;
- }
- /*!
- *\brief Save matchers from folder items
- *
- *\param fp File
- */
- static void prefs_matcher_save(FILE *fp)
- {
- GList *cur;
- for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
- Folder *folder;
- folder = (Folder *) cur->data;
- g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
- prefs_matcher_write_func, fp);
- }
-
- /* pre global rules */
- fprintf(fp, "[preglobal]\n");
- prefs_filtering_write(fp, pre_global_processing);
- fputc('\n', fp);
- /* post global rules */
- fprintf(fp, "[postglobal]\n");
- prefs_filtering_write(fp, post_global_processing);
- fputc('\n', fp);
-
- /* filtering rules */
- fprintf(fp, "[filtering]\n");
- prefs_filtering_write(fp, filtering_rules);
- fputc('\n', fp);
- }
- /*!
- *\brief Write filtering / matcher configuration file
- */
- void prefs_matcher_write_config(void)
- {
- gchar *rcpath;
- PrefFile *pfile;
- debug_print("Writing matcher configuration...\n");
- rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
- MATCHER_RC, NULL);
- if ((pfile = prefs_write_open(rcpath)) == NULL) {
- g_warning("failed to write configuration to file\n");
- g_free(rcpath);
- return;
- }
- prefs_matcher_save(pfile->fp);
- g_free(rcpath);
- if (prefs_file_close(pfile) < 0) {
- g_warning("failed to write configuration to file\n");
- return;
- }
- }
- /* ******************************************************************* */
- void matcher_add_rulenames(const gchar *rcpath)
- {
- gchar *newpath = g_strconcat(rcpath, ".new", NULL);
- FILE *src = g_fopen(rcpath, "rb");
- FILE *dst = g_fopen(newpath, "wb");
- gchar buf[BUFFSIZE];
- if (dst == NULL) {
- perror("fopen");
- g_free(newpath);
- return;
- }
- while (fgets (buf, sizeof(buf), src) != NULL) {
- if (strlen(buf) > 2 && buf[0] != '['
- && strncmp(buf, "rulename \"", 10)) {
- fwrite("rulename \"\" ",
- strlen("rulename \"\" "), 1, dst);
- }
- fwrite(buf, strlen(buf), 1, dst);
- }
- fclose(dst);
- fclose(src);
- move_file(newpath, rcpath, TRUE);
- g_free(newpath);
- }
- /*!
- *\brief Read matcher configuration
- */
- void prefs_matcher_read_config(void)
- {
- gchar *rcpath;
- gchar *rc_old_format;
- FILE *f;
- create_matchparser_hashtab();
- prefs_filtering_clear();
- rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MATCHER_RC, NULL);
- rc_old_format = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MATCHER_RC,
- ".pre_names", NULL);
-
- if (!is_file_exist(rc_old_format) && is_file_exist(rcpath)) {
- /* backup file with no rules names, in case
- * anything goes wrong */
- copy_file(rcpath, rc_old_format, FALSE);
- /* now hack the file in order to have it to the new format */
- matcher_add_rulenames(rcpath);
- }
-
- g_free(rc_old_format);
- f = g_fopen(rcpath, "rb");
- g_free(rcpath);
- if (f != NULL) {
- matcher_parser_start_parsing(f);
- fclose(matcher_parserin);
- }
- else {
- /* previous version compatibility */
- /* printf("reading filtering\n"); */
- rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
- FILTERING_RC, NULL);
- f = g_fopen(rcpath, "rb");
- g_free(rcpath);
-
- if (f != NULL) {
- matcher_parser_start_parsing(f);
- fclose(matcher_parserin);
- }
-
- /* printf("reading scoring\n"); */
- rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
- SCORING_RC, NULL);
- f = g_fopen(rcpath, "rb");
- g_free(rcpath);
-
- if (f != NULL) {
- matcher_parser_start_parsing(f);
- fclose(matcher_parserin);
- }
- }
- }
|