/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2008 Novell, Inc. (www.novell.com) * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: Srinivasa Ragavan */ /* This is a helper class for folders to implement the search function. * It implements enough to do basic searches on folders that can provide * an in-memory summary and a body index. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "camel-search-sql-sexp.h" #define d(x) /* x;printf("\n"); */ #ifdef TEST_MAIN #include typedef enum { CAMEL_SEARCH_MATCH_EXACT, CAMEL_SEARCH_MATCH_CONTAINS, CAMEL_SEARCH_MATCH_STARTS, CAMEL_SEARCH_MATCH_ENDS, CAMEL_SEARCH_MATCH_SOUNDEX } camel_search_match_t; gchar * camel_db_get_column_name (const gchar *raw_name); gchar * camel_db_sqlize_string (const gchar *string) { return sqlite3_mprintf ("%Q", string); } void camel_db_free_sqlized_string (gchar *string) { sqlite3_free (string); string = NULL; } #else #include "camel-db.h" #include "camel-folder-search.h" #include "camel-search-private.h" #endif static gchar * get_db_safe_string (const gchar *str) { gchar *tmp = camel_db_sqlize_string (str); gchar *ret; ret = g_strdup (tmp); camel_db_free_sqlized_string (tmp); return ret; } /* Configuration of your sexp expression */ static CamelSExpResult * func_and (CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { CamelSExpResult *r, *r1; GString *string; gint i; d (printf ("executing and: %d", argc)); string = g_string_new ("( "); for (i = 0; i < argc; i++) { r1 = camel_sexp_term_eval (f, argv[i]); if (r1->type != CAMEL_SEXP_RES_STRING) { camel_sexp_result_free (f, r1); continue; } if (r1->value.string && *r1->value.string) g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc - 1)) ? " AND ":""); camel_sexp_result_free (f, r1); } g_string_append (string, " )"); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); if (strlen (string->str) == 4) r->value.string = g_strdup (""); else r->value.string = string->str; g_string_free (string, FALSE); return r; } static CamelSExpResult * func_or (CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { CamelSExpResult *r, *r1; GString *string; gint i; d (printf ("executing or: %d", argc)); string = g_string_new ("( "); for (i = 0; i < argc; i++) { r1 = camel_sexp_term_eval (f, argv[i]); if (r1->type != CAMEL_SEXP_RES_STRING) { camel_sexp_result_free (f, r1); continue; } g_string_append_printf (string, "%s%s", r1->value.string, ((argc > 1) && (i != argc - 1)) ? " OR ":""); camel_sexp_result_free (f, r1); } g_string_append (string, " )"); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = string->str; g_string_free (string, FALSE); return r; } static CamelSExpResult * func_not (CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { CamelSExpResult *r = NULL, *r1; d (printf ("executing not: %d", argc)); r1 = camel_sexp_term_eval (f, argv[0]); if (r1->type == CAMEL_SEXP_RES_STRING) { r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); /* HACK: Fix and handle completed-on better. */ if (g_strcmp0 (r1->value.string, "( (usertags LIKE '%completed-on 0%' AND usertags LIKE '%completed-on%') )") == 0) r->value.string = g_strdup ("( (not (usertags LIKE '%completed-on 0%')) AND usertags LIKE '%completed-on%' )"); else r->value.string = g_strdup_printf ( "(NOT (%s))", r1->value.string); } camel_sexp_result_free (f, r1); return r; } /* this should support all arguments ...? */ static CamelSExpResult * eval_eq (struct _CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { struct _CamelSExpResult *r, *r1, *r2; r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); if (argc == 2) { GString *str = g_string_new ("( "); r1 = camel_sexp_term_eval (f, argv[0]); r2 = camel_sexp_term_eval (f, argv[1]); if (r1->type == CAMEL_SEXP_RES_INT) g_string_append_printf (str, "%d", r1->value.number); else if (r1->type == CAMEL_SEXP_RES_TIME) g_string_append_printf (str, "%" G_GINT64_FORMAT, (gint64) r1->value.time); else if (r1->type == CAMEL_SEXP_RES_STRING) g_string_append_printf (str, "%s", r1->value.string); if (g_str_equal (str->str, "( msgid") || g_str_equal (str->str, "( references")) { gboolean is_msgid = g_str_equal (str->str, "( msgid"); g_string_assign (str, "( part LIKE "); if (r2->type == CAMEL_SEXP_RES_STRING) { gchar *tmp, *safe; /* Expects CamelSummaryMessageID encoded as "%lu %lu", id.part.hi, id.part.lo. The 'msgid' is always the first, while 'references' is inside. */ /* Beware, the 'references' can return false positives, thus recheck returned UID-s. */ tmp = g_strdup_printf ("%s%s%%", is_msgid ? "" : "%", r2->value.string); safe = get_db_safe_string (tmp); g_string_append_printf (str, "%s", safe); g_free (safe); g_free (tmp); } else { g_warn_if_reached (); } } else if (!strstr (str->str, "completed-on") && !strstr (str->str, "follow-up")) { gboolean ut = FALSE; if (strstr (str->str, "usertags")) ut = TRUE; if (ut) g_string_append_printf (str, " LIKE "); else g_string_append_printf (str, " = "); if (r2->type == CAMEL_SEXP_RES_INT) g_string_append_printf (str, "%d", r2->value.number); if (r2->type == CAMEL_SEXP_RES_BOOL) g_string_append_printf (str, "%d", r2->value.boolean); else if (r2->type == CAMEL_SEXP_RES_TIME) g_string_append_printf (str, "%" G_GINT64_FORMAT, (gint64) r2->value.time); else if (r2->type == CAMEL_SEXP_RES_STRING) { gchar *tmp = g_strdup_printf ("%c%s%c", ut ? '%':' ', r2->value.string, ut ? '%':' '); gchar *safe = get_db_safe_string (tmp); g_string_append_printf (str, "%s", safe); g_free (safe); g_free (tmp); } } camel_sexp_result_free (f, r1); camel_sexp_result_free (f, r2); g_string_append (str, " )"); r->value.string = str->str; g_string_free (str, FALSE); } else { r->value.string = g_strdup ("(0)"); } return r; } static CamelSExpResult * eval_lt (struct _CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { struct _CamelSExpResult *r, *r1, *r2; r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); if (argc == 2) { GString *str = g_string_new ("( "); r1 = camel_sexp_term_eval (f, argv[0]); r2 = camel_sexp_term_eval (f, argv[1]); if (r1->type == CAMEL_SEXP_RES_INT) g_string_append_printf (str, "%d", r1->value.number); else if (r1->type == CAMEL_SEXP_RES_TIME) g_string_append_printf (str, "%" G_GINT64_FORMAT, (gint64) r1->value.time); else if (r1->type == CAMEL_SEXP_RES_STRING) g_string_append_printf (str, "%s", r1->value.string); g_string_append_printf (str, " < "); if (r2->type == CAMEL_SEXP_RES_INT) g_string_append_printf (str, "%d", r2->value.number); if (r2->type == CAMEL_SEXP_RES_BOOL) g_string_append_printf (str, "%d", r2->value.boolean); else if (r2->type == CAMEL_SEXP_RES_TIME) g_string_append_printf (str, "%" G_GINT64_FORMAT, (gint64) r2->value.time); else if (r2->type == CAMEL_SEXP_RES_STRING) g_string_append_printf (str, "%s", r2->value.string); camel_sexp_result_free (f, r1); camel_sexp_result_free (f, r2); g_string_append (str, " )"); r->value.string = str->str; g_string_free (str, FALSE); } return r; } /* this should support all arguments ...? */ static CamelSExpResult * eval_gt (struct _CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { struct _CamelSExpResult *r, *r1, *r2; r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); if (argc == 2) { GString *str = g_string_new ("( "); r1 = camel_sexp_term_eval (f, argv[0]); r2 = camel_sexp_term_eval (f, argv[1]); if (r1->type == CAMEL_SEXP_RES_INT) g_string_append_printf (str, "%d", r1->value.number); else if (r1->type == CAMEL_SEXP_RES_TIME) g_string_append_printf (str, "%" G_GINT64_FORMAT, (gint64) r1->value.time); else if (r1->type == CAMEL_SEXP_RES_STRING) g_string_append_printf (str, "%s", r1->value.string); g_string_append_printf (str, " > "); if (r2->type == CAMEL_SEXP_RES_INT) g_string_append_printf (str, "%d", r2->value.number); if (r2->type == CAMEL_SEXP_RES_BOOL) g_string_append_printf (str, "%d", r2->value.boolean); else if (r2->type == CAMEL_SEXP_RES_TIME) g_string_append_printf (str, "%" G_GINT64_FORMAT, (gint64) r2->value.time); else if (r2->type == CAMEL_SEXP_RES_STRING) g_string_append_printf (str, "%s", r2->value.string); camel_sexp_result_free (f, r1); camel_sexp_result_free (f, r2); g_string_append (str, " )"); r->value.string = str->str; g_string_free (str, FALSE); } return r; } static CamelSExpResult * match_all (struct _CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing match-all: %d", argc)); if (argc == 0) { r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = g_strdup ("1"); } else if (argv[0]->type != CAMEL_SEXP_TERM_BOOL) r = camel_sexp_term_eval (f, argv[0]); else { r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = g_strdup (argv[0]->value.boolean ? "1" : "0"); } return r; } static CamelSExpResult * match_threads (struct _CamelSExp *f, gint argc, struct _CamelSExpTerm **argv, gpointer data) { CamelSExpResult *r; gint i; GString *str = g_string_new ("( "); d (printf ("executing match-threads: %d", argc)); for (i = 1; i < argc; i++) { r = camel_sexp_term_eval (f, argv[i]); g_string_append_printf (str, "%s%s", r->value.string, ((argc > 1) && (i != argc - 1)) ? " AND ":""); camel_sexp_result_free (f, r); } g_string_append (str, " )"); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = str->str; g_string_free (str, FALSE); return r; } static CamelSExpResult * check_header (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data, camel_search_match_t how) { CamelSExpResult *r; gchar *str = NULL; d (printf ("executing check-header %d\n", how)); /* are we inside a match-all? */ if (argc > 1 && argv[0]->type == CAMEL_SEXP_RES_STRING) { gchar *headername; gint i; /* only a subset of headers are supported .. */ headername = camel_db_get_column_name (argv[0]->value.string); if (!headername) { gboolean *pcontains_unknown_column = (gboolean *) data; *pcontains_unknown_column = TRUE; headername = g_strdup ("unknown"); } /* performs an OR of all words */ for (i = 1; i < argc; i++) { if (argv[i]->type == CAMEL_SEXP_RES_STRING) { gchar *value = NULL, *tstr = NULL; if (argv[i]->value.string[0] == 0) continue; if (how == CAMEL_SEARCH_MATCH_CONTAINS || how == CAMEL_SEARCH_MATCH_WORD) { tstr = g_strdup_printf ("%c%s%c", '%', argv[i]->value.string, '%'); value = get_db_safe_string (tstr); g_free (tstr); } else if (how == CAMEL_SEARCH_MATCH_ENDS) { tstr = g_strdup_printf ("%c%s", '%', argv[i]->value.string); value = get_db_safe_string (tstr); g_free (tstr); } else if (how == CAMEL_SEARCH_MATCH_STARTS) { tstr = g_strdup_printf ("%s%c", argv[i]->value.string, '%'); value = get_db_safe_string (tstr); g_free (tstr); } else if (how == CAMEL_SEARCH_MATCH_EXACT) { tstr = g_strdup_printf ("%c%s%c", '%', argv[i]->value.string, '%'); value = get_db_safe_string (tstr); g_free (tstr); } str = g_strdup_printf ("(%s IS NOT NULL AND %s LIKE %s)", headername, headername, value); g_free (value); } } g_free (headername); } /* TODO: else, find all matches */ r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = str; return r; } static CamelSExpResult * header_contains (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { d (printf ("executing header-contains: %d", argc)); return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_CONTAINS); } static CamelSExpResult * header_has_words (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { d (printf ("executing header-has-word: %d", argc)); return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_WORD); } static CamelSExpResult * header_matches (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { d (printf ("executing header-matches: %d", argc)); return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_EXACT); } static CamelSExpResult * header_starts_with (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { d (printf ("executing header-starts-with: %d", argc)); return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_STARTS); } static CamelSExpResult * header_ends_with (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { d (printf ("executing header-ends-with: %d", argc)); return check_header (f, argc, argv, data, CAMEL_SEARCH_MATCH_ENDS); } static CamelSExpResult * header_exists (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; gchar *headername; d (printf ("executing header-exists: %d", argc)); headername = camel_db_get_column_name (argv[0]->value.string); if (!headername) { gboolean *pcontains_unknown_column = (gboolean *) data; *pcontains_unknown_column = TRUE; headername = g_strdup ("unknown"); } r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = g_strdup_printf ("(%s NOTNULL)", headername); g_free (headername); return r; } static CamelSExpResult * user_tag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing user-tag: %d", argc)); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); /* Hacks no otherway to fix these really :( */ if (g_strcmp0 (argv[0]->value.string, "completed-on") == 0) r->value.string = g_strdup_printf ("(usertags LIKE '%ccompleted-on 0%c' AND usertags LIKE '%ccompleted-on%c')", '%', '%', '%', '%'); else if (g_strcmp0 (argv[0]->value.string, "follow-up") == 0) r->value.string = g_strdup_printf ("usertags NOT LIKE '%cfollow-up%c'", '%', '%'); else r->value.string = g_strdup ("usertags"); return r; } static CamelSExpResult * user_flag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; gchar *tstr, *qstr; d (printf ("executing user-flag: %d", argc)); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); if (argc != 1) { r->value.string = g_strdup ("(0)"); } else { tstr = g_strdup_printf ("%s", argv[0]->value.string); qstr = get_db_safe_string (tstr); g_free (tstr); r->value.string = g_strdup_printf ("(labels MATCH %s)", qstr); g_free (qstr); } return r; } static CamelSExpResult * system_flag (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; gchar *tstr; d (printf ("executing system-flag: %d", argc)); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); if (argc != 1) { r->value.string = g_strdup ("(0)"); } else { tstr = camel_db_get_column_name (argv[0]->value.string); if (!tstr) { gboolean *pcontains_unknown_column = (gboolean *) data; *pcontains_unknown_column = TRUE; tstr = g_strdup ("unknown"); } r->value.string = g_strdup_printf ("(%s = 1)", tstr); g_free (tstr); } return r; } static CamelSExpResult * get_sent_date (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing get-sent-date\n")); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = g_strdup ("dsent"); return r; } static CamelSExpResult * get_received_date (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing get-received-date\n")); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = g_strdup ("dreceived"); return r; } static CamelSExpResult * get_current_date (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing get-current-date\n")); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT); r->value.number = time (NULL); return r; } static CamelSExpResult * get_relative_months (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing get-relative-months\n")); if (argc != 1 || argv[0]->type != CAMEL_SEXP_RES_INT) { r = camel_sexp_result_new (f, CAMEL_SEXP_RES_BOOL); r->value.boolean = FALSE; g_debug ("%s: Expecting 1 argument, an integer, but got %d arguments", G_STRFUNC, argc); } else { r = camel_sexp_result_new (f, CAMEL_SEXP_RES_INT); r->value.number = camel_folder_search_util_add_months (time (NULL), argv[0]->value.number); } return r; } static CamelSExpResult * get_size (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; d (printf ("executing get-size\n")); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); r->value.string = g_strdup ("size/1024"); return r; } static CamelSExpResult * sql_exp (struct _CamelSExp *f, gint argc, struct _CamelSExpResult **argv, gpointer data) { CamelSExpResult *r; gint i; GString *str = g_string_new (NULL); d (printf ("executing sql-exp\n")); r = camel_sexp_result_new (f, CAMEL_SEXP_RES_STRING); for (i = 0; i < argc; i++) { if (argv[i]->type == CAMEL_SEXP_RES_STRING && argv[i]->value.string) g_string_append (str, argv[i]->value.string); } r->value.string = str->str; g_string_free (str, FALSE); return r; } /* 'builtin' functions */ static struct { const gchar *name; CamelSExpFunc func; guint immediate :1; } symbols[] = { { "and", (CamelSExpFunc) func_and, 1 }, { "or", (CamelSExpFunc) func_or, 1}, { "not", (CamelSExpFunc) func_not, 1}, { "=", (CamelSExpFunc) eval_eq, 1}, { ">", (CamelSExpFunc) eval_gt, 1}, { "<", (CamelSExpFunc) eval_lt, 1}, { "match-all", (CamelSExpFunc) match_all, 1 }, { "match-threads", (CamelSExpFunc) match_threads, 1 }, /* { "body-contains", body_contains}, */ /* We don't store body on the db. */ { "header-contains", header_contains, 0}, { "header-has-words", header_has_words, 0}, { "header-matches", header_matches, 0}, { "header-starts-with", header_starts_with, 0}, { "header-ends-with", header_ends_with, 0}, { "header-exists", header_exists, 0}, { "user-tag", user_tag, 0}, { "user-flag", user_flag, 0}, { "system-flag", system_flag, 0}, { "get-sent-date", get_sent_date, 0}, { "get-received-date", get_received_date, 0}, { "get-current-date", get_current_date, 0}, { "get-relative-months", get_relative_months, 0}, { "get-size", get_size, 0}, { "sql-exp", sql_exp, 0}, /* { "uid", CAMEL_STRUCT_OFFSET(CamelFolderSearchClass, uid), 1 }, */ }; /** * camel_sexp_to_sql_sexp: * * Since: 2.26 **/ gchar * camel_sexp_to_sql_sexp (const gchar *sql) { CamelSExp *sexp; CamelSExpResult *r; gint i; gchar *res = NULL; gboolean contains_unknown_column = FALSE; sexp = camel_sexp_new (); for (i = 0; i < G_N_ELEMENTS (symbols); i++) { if (symbols[i].immediate) camel_sexp_add_ifunction (sexp, 0, symbols[i].name, (CamelSExpIFunc) symbols[i].func, &contains_unknown_column); else camel_sexp_add_function ( sexp, 0, symbols[i].name, symbols[i].func, &contains_unknown_column); } camel_sexp_input_text (sexp, sql, strlen (sql)); if (camel_sexp_parse (sexp)) { g_object_unref (sexp); return NULL; } r = camel_sexp_eval (sexp); if (!r) { g_object_unref (sexp); return NULL; } if (!contains_unknown_column && r->type == CAMEL_SEXP_RES_STRING) { res = g_strdup (r->value.string); } camel_sexp_result_free (sexp, r); g_object_unref (sexp); return res; } #ifdef TEST_MAIN /* * * (and (match-all (and (not (system-flag "deleted")) (not (system-flag "junk")))) * (and (or * * (match-all (not (system-flag "Attachments"))) * * ) * )) * *" * replied INTEGER , (match-all (system-flag "Answered")) * size INTEGER , (match-all (< (get-size) 100)) * dsent NUMERIC , (match-all (< (get-sent-date) (- (get-current-date) 10))) * dreceived NUMERIC , (match-all (< (get-received-date) (- (get-current-date) 10))) * //mlist TEXT , x-camel-mlist (match-all (header-matches "x-camel-mlist" "gnome.org")) * //attachment, system-flag "Attachments" (match-all (system-flag "Attachments")) * //followup_flag TEXT , (match-all (not (= (user-tag "follow-up") ""))) * //followup_completed_on TEXT , (match-all (not (= (user-tag "completed-on") ""))) * //followup_due_by TEXT ," //NOTREQD */ gchar * camel_db_get_column_name (const gchar *raw_name) { /* d(g_print ("\n\aRAW name is : [%s] \n\a", raw_name)); */ if (!g_ascii_strcasecmp (raw_name, "Subject")) return g_strdup ("subject"); else if (!g_ascii_strcasecmp (raw_name, "from")) return g_strdup ("mail_from"); else if (!g_ascii_strcasecmp (raw_name, "Cc")) return g_strdup ("mail_cc"); else if (!g_ascii_strcasecmp (raw_name, "To")) return g_strdup ("mail_to"); else if (!g_ascii_strcasecmp (raw_name, "Flagged")) return g_strdup ("important"); else if (!g_ascii_strcasecmp (raw_name, "deleted")) return g_strdup ("deleted"); else if (!g_ascii_strcasecmp (raw_name, "junk")) return g_strdup ("junk"); else if (!g_ascii_strcasecmp (raw_name, "Answered")) return g_strdup ("replied"); else if (!g_ascii_strcasecmp (raw_name, "Seen")) return g_strdup ("read"); else if (!g_ascii_strcasecmp (raw_name, "user-tag")) return g_strdup ("usertags"); else if (!g_ascii_strcasecmp (raw_name, "user-flag")) return g_strdup ("labels"); else if (!g_ascii_strcasecmp (raw_name, "Attachments")) return g_strdup ("attachment"); else if (!g_ascii_strcasecmp (raw_name, "x-camel-mlist")) return g_strdup ("mlist"); else { /* Let it crash for all unknown columns for now. * We need to load the messages into memory and search etc. * We should extend this for camel-folder-search system flags search as well * otherwise, search-for-signed-messages will not work etc.*/ return g_strdup (raw_name); } } gint main () { gint i = 0; gchar *txt[] = { #if 0 "(match-all (header-contains \"From\" \"org\"))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (or (header-ends-with \"To\" \"novell.com\") (header-ends-with \"Cc\" \"novell.com\"))) (match-all (or (= (user-tag \"label\") \"work\") (user-flag \"work\"))) )))", "(and (and (match-all (header-contains \"From\" \"org\")) ) (match-all (not (system-flag \"junk\"))))", "(and (and (match-all (header-contains \"From\" \"org\"))) (and (match-all (not (system-flag \"junk\"))) (and (or (match-all (header-contains \"Subject\" \"test\")) (match-all (header-contains \"From\" \"test\"))))))", "(and (and (match-all (header-exists \"From\")) ) (match-all (not (system-flag \"junk\"))))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (header-contains \"Subject\" \"org\")) (match-all (header-contains \"From\" \"org\")) (match-all (system-flag \"Flagged\")) (match-all (system-flag \"Seen\")) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (or (header-ends-with \"To\" \"novell.com\") (header-ends-with \"Cc\" \"novell.com\"))) (= (user-tag \"label\") \"work\") )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (or (header-ends-with \"To\" \"novell.com\") (header-ends-with \"Cc\" \"novell.com\"))) (user-flag \"work\") )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (or (header-ends-with \"To\" \"novell.com\") (header-ends-with \"Cc\" \"novell.com\"))) (user-flag (+ \"$Label\" \"work\")) )))" "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (not (= (user-tag \"follow-up\") \"\"))) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (= (user-tag \"follow-up\") \"\")) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (not (= (user-tag \"completed-on\") \"\"))) )))", "(match-all (and (match-all #t) (system-flag \"deleted\")))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (or (= (user-tag \"label\") \"important\") (user-flag (+ \"$Label\" \"important\")) (user-flag \"important\"))) )))", "(or (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")) (not (system-flag \"Attachments\")) (not (system-flag \"Answered\")))) (and (or (match-all (= (user-tag \"completed-on\") \"\")) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (= (user-tag \"completed-on\") \"\")) (match-all (= (user-tag \"follow-up\") \"\")) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (> (get-sent-date) (- (get-current-date) 100))) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (< (get-sent-date) (+ (get-current-date) 100))) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (not (= (get-sent-date) 1216146600))) )))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (= (get-sent-date) 1216146600)) )))" , "(match-threads \"all\" (or (match-all (header-contains \"From\" \"@edesvcs.com\")) (match-all (or (header-contains \"To\" \"@edesvcs.com\") (header-contains \"Cc\" \"@edesvcs.com\"))) ))", "(match-all (not (system-flag \"deleted\")))", "(match-all (system-flag \"seen\"))", "(match-all (and (match-all #t) (system-flag \"deleted\")))", "(match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))", "(and (or (match-all (header-contains \"Subject\" \"lin\")) ) (and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (header-contains \"Subject\" \"case\")) (match-all (header-contains \"From\" \"case\"))))))", "(and ( match-all(or (match-all (header-contains \"Subject\" \"lin\")) (match-all (header-contains \"From\" \"in\")))) (and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (header-contains \"Subject\" \"proc\")) (match-all (header-contains \"From\" \"proc\"))))))", "(and (or (match-all (header-contains \"Subject\" \"[LDTP-NOSIP]\")) ) (and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all (header-contains \"Subject\" \"vamsi\")) (match-all (header-contains \"From\" \"vamsi\"))))))", /* Last one doesn't work so well and fails on one case. But I doubt, you can create a query like that in Evo. */ "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (match-all (or (= (user-tag \"label\") \"_office\") (user-flag \"$Label_office\") (user-flag \"_office\"))))", "(and (and (match-all #t))(and(match-all #t)))", "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (and (match-all (header-contains \"Subject\" \"mysubject\")) (match-all (not (header-matches \"From\" \"mysender\"))) (match-all (= (get-sent-date) (+ (get-current-date) 1))) (match-all (= (get-received-date) (- (get-current-date) 604800))) (match-all (or (= (user-tag \"label\") \"important\") (user-flag (+ \"$Label\" \"important\")) (match-all (< (get-size) 7000)) (match-all (not (= (get-sent-date) 1216146600))) (match-all (> (cast-int (user-tag \"score\")) 3)) (user-flag \"important\"))) (match-all (system-flag \"Deleted\")) (match-all (not (= (user-tag \"follow-up\") \"\"))) (match-all (= (user-tag \"completed-on\") \"\")) (match-all (system-flag \"Attachments\")) (match-all (header-contains \"x-camel-mlist\" \"evo-hackers\")) )))", "(and (or (match-all (or (= (user-tag \"label\") \"important\") (user-flag (+ \"$Label\" \"important\")) (user-flag \"important\"))) (match-all (or (= (user-tag \"label\") \"work\") (user-flag (+ \"$Label\" \"work\")) (user-flag \"work\"))) (match-all (or (= (user-tag \"label\") \"personal\") (user-flag (+ \"$Label\" \"personal\")) (user-flag \"personal\"))) (match-all (or (= (user-tag \"label\") \"todo\") (user-flag (+ \"$Label\" \"todo\")) (user-flag \"todo\"))) (match-all (or (= (user-tag \"label\") \"later\") (user-flag (+ \"$Label\" \"later\")) (user-flag \"later\"))) ) (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))))", "(or (header-matches \"to\" \"maw@ximian.com\") (header-matches \"to\" \"mw@ximian.com\") (header-matches \"to\" \"maw@novell.com\") (header-matches \"to\" \"maw.AMERICAS3.AMERICAS@novell.com\") (header-matches \"cc\" \"maw@ximian.com\") (header-matches \"cc\" \"mw@ximian.com\") (header-matches \"cc\" \"maw@novell.com\") (header-matches \"cc\" \"maw.AMERICAS3.AMERICAS@novell.com\"))", "(not (or (header-matches \"from\" \"bugzilla-daemon@bugzilla.ximian.com\") (header-matches \"from\" \"bugzilla-daemon@bugzilla.gnome.org\") (header-matches \"from\" \"bugzilla_noreply@novell.com\") (header-matches \"from\" \"bugzilla-daemon@mozilla.org\") (header-matches \"from\" \"root@dist.suse.de\") (header-matches \"from\" \"root@hilbert3.suse.de\") (header-matches \"from\" \"root@hilbert4.suse.de\") (header-matches \"from\" \"root@hilbert5.suse.de\") (header-matches \"from\" \"root@hilbert6.suse.de\") (header-matches \"from\" \"root@suse.de\") (header-matches \"from\" \"swamp_noreply@suse.de\") (and (header-matches \"from\" \"hermes@opensuse.org\") (header-starts-with \"subject\" \"submit-Request\"))))", "(and (match-threads \"replies_parents\" (and (match-all (or (header-matches \"to\" \"maw@ximian.com\") (header-matches \"to\" \"mw@ximian.com\") (header-matches \"to\" \"maw@novell.com\") (header-matches \"to\" \"maw.AMERICAS3.AMERICAS@novell.com\") (header-matches \"cc\" \"maw@ximian.com\") (header-matches \"cc\" \"mw@ximian.com\") (header-matches \"cc\" \"maw@novell.com\") (header-matches \"cc\" \"maw.AMERICAS3.AMERICAS@novell.com\"))) (match-all (not (or (header-matches \"from\" \"bugzilla-daemon@bugzilla.ximian.com\") (header-matches \"from\" \"bugzilla-daemon@bugzilla.gnome.org\") (header-matches \"from\" \"bugzilla_noreply@novell.com\") (header-matches \"from\" \"bugzilla-daemon@mozilla.org\") (header-matches \"from\" \"root@dist.suse.de\") (header-matches \"from\" \"root@hilbert3.suse.de\") (header-matches \"from\" \"root@hilbert4.suse.de\") (header-matches \"from\" \"root@hilbert5.suse.de\") (header-matches \"from\" \"root@hilbert6.suse.de\") (header-matches \"from\" \"root@suse.de\") (header-matches \"from\" \"swamp_noreply@suse.de\") (and (header-matches \"from\" \"hermes@opensuse.org\") (header-starts-with \"subject\" \"submit-Request\"))))) (match-all (> (get-sent-date) (- (get-current-date) 1209600))) )) (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))))", "and ((match-all (system-flag \"Deleted\")) (match-all (system-flag \"junk\")))", "(and (match-threads \"replies_parents\" (and (match-all (or (header-matches \"to\" \"maw@ximian.com\")))))))", "(and (sql-exp \"folder_key = 'ASDGASd' AND folder_key = 'DSFWEA'\") (match-threads \"replies_parents\" (and (match-all (or (header-matches \"to\" \"maw@ximian.com\")))))))" #endif "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\")))) (and (or (match-all list-post.*zypp-devel) ) ))" }; for (i = 0; i < G_N_ELEMENTS (txt); i++) { gchar *sql = NULL; printf ("Q: %s\n\"%c\"\n", txt[i], 40); sql = camel_sexp_to_sql_sexp (txt[i]); printf ("A: %s\n\n\n", sql); g_free (sql); } } #endif