/*
* 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);
}