/* * Copyright (C) 2014 Red Hat, Inc. (www.redhat.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 . * */ #include "evolution-config.h" #include #include #include #include #include #include #include "e-mail-free-form-exp.h" static gchar * mail_ffe_build_header_sexp (const gchar *word, const gchar *options, const gchar * const *header_names) { GString *sexp = NULL, *encoded_word; const gchar *compare_type = NULL; gint ii; g_return_val_if_fail (header_names != NULL, NULL); g_return_val_if_fail (header_names[0] != NULL, NULL); if (!word) return NULL; if (options) { struct _KnownOptions { const gchar *compare_type; const gchar *alt_name; } known_options[] = { { "contains", "c" }, { "has-words", "w" }, { "matches", "m" }, { "starts-with", "sw" }, { "ends-with", "ew" }, { "soundex", "se" }, { "regex", "r" }, { "full-regex", "fr" } }; for (ii = 0; ii < G_N_ELEMENTS (known_options); ii++) { if (g_ascii_strcasecmp (options, known_options[ii].compare_type) == 0 || (known_options[ii].alt_name && g_ascii_strcasecmp (options, known_options[ii].alt_name) == 0)) { compare_type = known_options[ii].compare_type; break; } } } if (!compare_type) compare_type = "contains"; encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); if (!header_names[1]) { sexp = g_string_new (""); } else { sexp = g_string_new ("(or "); } for (ii = 0; header_names[ii]; ii++) { g_string_append_printf (sexp, "(match-all (header-%s \"%s\" %s))", compare_type, header_names[ii], encoded_word->str); } if (header_names[1]) g_string_append (sexp, ")"); g_string_free (encoded_word, TRUE); return sexp ? g_string_free (sexp, FALSE) : NULL; } static gchar * mail_ffe_recips (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { "To", "Cc", "Subject", NULL }; /* Include Subject only in the default expression. */ if (!hint) header_names[2] = NULL; return mail_ffe_build_header_sexp (word, options, header_names); } static gchar * mail_ffe_from (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { "From", NULL }; return mail_ffe_build_header_sexp (word, options, header_names); } static gchar * mail_ffe_to (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { "To", NULL }; return mail_ffe_build_header_sexp (word, options, header_names); } static gchar * mail_ffe_cc (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { "Cc", NULL }; return mail_ffe_build_header_sexp (word, options, header_names); } static gchar * mail_ffe_subject (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { "Subject", NULL }; return mail_ffe_build_header_sexp (word, options, header_names); } static gchar * mail_ffe_list (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { "x-camel-mlist", NULL }; return mail_ffe_build_header_sexp (word, options, header_names); } static gchar * mail_ffe_header (const gchar *word, const gchar *options, const gchar *hint) { const gchar *header_names[] = { NULL, NULL }; const gchar *equal; gchar *header_name, *sexp; equal = word ? strchr (word, '=') : NULL; if (!equal) return NULL; header_name = g_strndup (word, equal - word); header_names[0] = header_name; sexp = mail_ffe_build_header_sexp (equal + 1, options, header_names); g_free (header_name); return sexp; } static gchar * mail_ffe_exists (const gchar *word, const gchar *options, const gchar *hint) { GString *encoded_word; gchar *sexp; if (!word) return NULL; encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); sexp = g_strdup_printf ("(match-all (header-exists %s))", encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gchar * mail_ffe_tag (const gchar *word, const gchar *options, const gchar *hint) { GString *encoded_word; gchar *sexp; if (!word) return NULL; encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); sexp = g_strdup_printf ("(match-all (not (= (user-tag %s) \"\")))", encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gchar * mail_ffe_flag (const gchar *word, const gchar *options, const gchar *hint) { const gchar *system_flags[] = { /* Translators: This is a name of a flag, the same as all strings in the 'ffe' context. The translated value should not contain spaces. */ NC_("ffe", "Answered"), NC_("ffe", "Deleted"), NC_("ffe", "Draft"), NC_("ffe", "Flagged"), NC_("ffe", "Seen"), NC_("ffe", "Attachment") }; GString *encoded_word; gchar *sexp = NULL; gint ii; if (!word) return NULL; encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); for (ii = 0; ii < G_N_ELEMENTS (system_flags); ii++) { if (g_ascii_strcasecmp (word, system_flags[ii]) == 0 || g_ascii_strcasecmp (word, g_dpgettext2 (NULL, "ffe", system_flags[ii])) == 0) { sexp = g_strdup_printf ("(match-all (system-flag \"%s\"))", system_flags[ii]); break; } } if (!sexp) sexp = g_strdup_printf ("(match-all (not (= (user-tag %s) \"\")))", encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gchar * mail_ffe_label (const gchar *word, const gchar *options, const gchar *hint) { GString *encoded_word; gchar *sexp; if (!word) return NULL; encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); sexp = g_strdup_printf ("(match-all (or ((= (user-tag \"label\") %s) (user-flag (+ \"$Label\" %s)) (user-flag %s)))", encoded_word->str, encoded_word->str, encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gchar * mail_ffe_size (const gchar *word, const gchar *options, const gchar *hint) { GString *encoded_word; gchar *sexp; const gchar *cmp = "="; if (!word) return NULL; if (options) { if (g_ascii_strcasecmp (options, "<") == 0 || g_ascii_strcasecmp (options, ">") == 0) cmp = options; } encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); sexp = g_strdup_printf ("(match-all (%s (get-size) (cast-int %s)))", cmp, encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gchar * mail_ffe_score (const gchar *word, const gchar *options, const gchar *hint) { GString *encoded_word; gchar *sexp; const gchar *cmp = "="; if (!word) return NULL; if (options) { if (g_ascii_strcasecmp (options, "<") == 0 || g_ascii_strcasecmp (options, ">") == 0) cmp = options; } encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); sexp = g_strdup_printf ("(match-all (%s (cast-int (user-tag \"score\")) (cast-int %s)))", cmp, encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gchar * mail_ffe_body (const gchar *word, const gchar *options, const gchar *hint) { GString *encoded_word; gchar *sexp; const gchar *cmp = "contains"; if (!word) return NULL; if (options) { if (g_ascii_strcasecmp (options, "regex") == 0 || g_ascii_strcasecmp (options, "re") == 0 || g_ascii_strcasecmp (options, "r") == 0) cmp = "regex"; } encoded_word = g_string_new (""); camel_sexp_encode_string (encoded_word, word); sexp = g_strdup_printf ("(match-all (body-%s %s))", cmp, encoded_word->str); g_string_free (encoded_word, TRUE); return sexp; } static gboolean mail_ffe_decode_date_time (const gchar *word, GTimeVal *tv) { struct tm tm; g_return_val_if_fail (word != NULL, FALSE); g_return_val_if_fail (tv != NULL, FALSE); /* YYYY-MM-DD */ if (strlen (word) == 10 && word[4] == '-' && word[7] == '-') { gint yy, mm, dd; yy = atoi (word); mm = atoi (word + 5); dd = atoi (word + 8); if (g_date_valid_dmy (dd, mm, yy)) { GDate *date; date = g_date_new_dmy (dd, mm, yy); g_date_to_struct_tm (date, &tm); g_date_free (date); tv->tv_sec = mktime (&tm); tv->tv_usec = 0; return TRUE; } } if (g_time_val_from_iso8601 (word, tv)) return TRUE; if (e_time_parse_date_and_time (word, &tm) == E_TIME_PARSE_OK || e_time_parse_date (word, &tm) == E_TIME_PARSE_OK) { tv->tv_sec = mktime (&tm); tv->tv_usec = 0; return TRUE; } return FALSE; } static gchar * mail_ffe_process_date (const gchar *get_date_fnc, const gchar *word, const gchar *options) { gint64 rel_days; gchar *endptr = NULL; const gchar *op = ">"; GTimeVal tv; g_return_val_if_fail (get_date_fnc != NULL, NULL); if (options) { if (g_ascii_strcasecmp (options, "<") == 0) { op = "<"; } else if (g_ascii_strcasecmp (options, "=") == 0) { op = "="; } else if (g_ascii_strcasecmp (options, ">") == 0) { op = ">"; } } rel_days = g_ascii_strtoll (word, &endptr, 10); if (rel_days != 0 && endptr && !*endptr) { return g_strdup_printf ("(match-all (%s (%s) (%s (get-current-date) %" G_GINT64_FORMAT ")))", op, get_date_fnc, rel_days < 0 ? "+" : "-", (rel_days < 0 ? -1 : 1) * rel_days * 24 * 60 * 60); } if (!mail_ffe_decode_date_time (word, &tv)) return g_strdup_printf ("(match-all (%s (%s) (get-current-date)))", op, get_date_fnc); return g_strdup_printf ("(match-all (%s (%s) %" G_GINT64_FORMAT "))", op, get_date_fnc, (gint64) tv.tv_sec); } static gchar * mail_ffe_sent (const gchar *word, const gchar *options, const gchar *hint) { if (!word) return NULL; return mail_ffe_process_date ("get-sent-date", word, options); } static gchar * mail_ffe_received (const gchar *word, const gchar *options, const gchar *hint) { if (!word) return NULL; return mail_ffe_process_date ("get-received-date", word, options); } static gchar * mail_ffe_attachment (const gchar *word, const gchar *options, const gchar *hint) { gboolean is_neg = FALSE; if (!word) return NULL; if (g_ascii_strcasecmp (word, "no") == 0 || g_ascii_strcasecmp (word, "false") == 0 || g_ascii_strcasecmp (word, C_("ffe", "no")) == 0 || g_ascii_strcasecmp (word, C_("ffe", "false")) == 0 || g_ascii_strcasecmp (word, "0") == 0) { is_neg = TRUE; } return g_strdup_printf ("(match-all %s(system-flag \"Attachment\")%s)", is_neg ? "(not " : "", is_neg ? ")" : ""); } static const EFreeFormExpSymbol mail_ffe_symbols[] = { { "", "1", mail_ffe_recips }, { "from:f", NULL, mail_ffe_from }, { "to:t", NULL, mail_ffe_to }, { "cc:c:", NULL, mail_ffe_cc }, { "recips:r", NULL, mail_ffe_recips }, { "subject:s", NULL, mail_ffe_subject }, { "list", NULL, mail_ffe_list }, { "header:h", NULL, mail_ffe_header }, { "exists:e", NULL, mail_ffe_exists }, { "tag", NULL, mail_ffe_tag }, { "flag", NULL, mail_ffe_flag }, { "label:l", NULL, mail_ffe_label }, { "size:sz", NULL, mail_ffe_size }, { "score:sc", NULL, mail_ffe_score }, { "body:b", NULL, mail_ffe_body }, { "sent", NULL, mail_ffe_sent }, { "received:rcv", NULL, mail_ffe_received }, { "attachment:a", NULL, mail_ffe_attachment }, { NULL, NULL, NULL} }; static gchar * get_filter_input_value (EFilterPart *part, const gchar *name) { EFilterElement *elem; EFilterInput *input; GString *value; GList *link; g_return_val_if_fail (part != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); elem = e_filter_part_find_element (part, name); g_return_val_if_fail (elem != NULL, NULL); g_return_val_if_fail (E_IS_FILTER_INPUT (elem), NULL); input = E_FILTER_INPUT (elem); value = g_string_new (""); for (link = input->values; link; link = g_list_next (link)) { const gchar *val = link->data; if (val && *val) { if (value->len > 0) g_string_append_c (value, ' '); g_string_append (value, val); } } return g_string_free (value, FALSE); } void e_mail_free_form_exp_to_sexp (EFilterElement *element, GString *out, EFilterPart *part) { gchar *ffe, *sexp; ffe = get_filter_input_value (part, "ffe"); g_return_if_fail (ffe != NULL); sexp = e_free_form_exp_to_sexp (ffe, mail_ffe_symbols); if (sexp) g_string_append (out, sexp); g_free (sexp); g_free (ffe); }