/* * This program 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; either * version 2 of the License, or (at your option) version 3. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Not Zed * Jeffrey Stedfast * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "em-vfolder-context.h" #include "em-vfolder-rule.h" #define EM_VFOLDER_RULE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), EM_TYPE_VFOLDER_RULE, EMVFolderRulePrivate)) #define EM_VFOLDER_RULE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), EM_TYPE_VFOLDER_RULE, EMVFolderRulePrivate)) struct _EMVFolderRulePrivate { em_vfolder_rule_with_t with; GQueue sources; /* uri's of the source folders */ gboolean autoupdate; GHashTable *include_subfolders; }; static gint validate (EFilterRule *, EAlert **alert); static gint vfolder_eq (EFilterRule *fr, EFilterRule *cm); static xmlNodePtr xml_encode (EFilterRule *); static gint xml_decode (EFilterRule *, xmlNodePtr, ERuleContext *f); static void rule_copy (EFilterRule *dest, EFilterRule *src); static GtkWidget *get_widget (EFilterRule *fr, ERuleContext *f); /* DO NOT internationalise these strings */ static const gchar *with_names[] = { "specific", "local_remote_active", "remote_active", "local" }; G_DEFINE_TYPE ( EMVFolderRule, em_vfolder_rule, E_TYPE_FILTER_RULE) static void vfolder_rule_finalize (GObject *object) { EMVFolderRule *rule = EM_VFOLDER_RULE (object); gchar *uri; while ((uri = g_queue_pop_head (&rule->priv->sources)) != NULL) g_free (uri); g_hash_table_destroy (rule->priv->include_subfolders); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (em_vfolder_rule_parent_class)->finalize (object); } static void em_vfolder_rule_class_init (EMVFolderRuleClass *class) { GObjectClass *object_class; EFilterRuleClass *filter_rule_class; g_type_class_add_private (class, sizeof (EMVFolderRulePrivate)); object_class = G_OBJECT_CLASS (class); object_class->finalize = vfolder_rule_finalize; filter_rule_class = E_FILTER_RULE_CLASS (class); filter_rule_class->validate = validate; filter_rule_class->eq = vfolder_eq; filter_rule_class->xml_encode = xml_encode; filter_rule_class->xml_decode = xml_decode; filter_rule_class->copy = rule_copy; filter_rule_class->get_widget = get_widget; } static void em_vfolder_rule_init (EMVFolderRule *rule) { rule->priv = EM_VFOLDER_RULE_GET_PRIVATE (rule); rule->priv->with = EM_VFOLDER_RULE_WITH_SPECIFIC; rule->priv->autoupdate = TRUE; /* it's using pointers from priv::sources, and those * included has include_subfolders set to true */ rule->priv->include_subfolders = g_hash_table_new ( (GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal); rule->rule.source = g_strdup ("incoming"); } EFilterRule * em_vfolder_rule_new (void) { return g_object_new ( EM_TYPE_VFOLDER_RULE, NULL); } void em_vfolder_rule_add_source (EMVFolderRule *rule, const gchar *uri) { g_return_if_fail (EM_IS_VFOLDER_RULE (rule)); g_return_if_fail (uri); g_queue_push_tail (&rule->priv->sources, g_strdup (uri)); e_filter_rule_emit_changed (E_FILTER_RULE (rule)); } const gchar * em_vfolder_rule_find_source (EMVFolderRule *rule, const gchar *uri) { GList *link; g_return_val_if_fail (EM_IS_VFOLDER_RULE (rule), NULL); /* only does a simple string or address comparison, should * probably do a decoded url comparison */ link = g_queue_find_custom ( &rule->priv->sources, uri, (GCompareFunc) strcmp); return (link != NULL) ? link->data : NULL; } void em_vfolder_rule_remove_source (EMVFolderRule *rule, const gchar *uri) { gchar *found; g_return_if_fail (EM_IS_VFOLDER_RULE (rule)); found =(gchar *) em_vfolder_rule_find_source (rule, uri); if (found != NULL) { g_queue_remove (&rule->priv->sources, found); g_hash_table_remove (rule->priv->include_subfolders, found); g_free (found); e_filter_rule_emit_changed (E_FILTER_RULE (rule)); } } const gchar * em_vfolder_rule_next_source (EMVFolderRule *rule, const gchar *last) { GList *link; if (last == NULL) { link = g_queue_peek_head_link (&rule->priv->sources); } else { link = g_queue_find (&rule->priv->sources, last); if (link == NULL) link = g_queue_peek_head_link (&rule->priv->sources); else link = g_list_next (link); } return (link != NULL) ? link->data : NULL; } GQueue * em_vfolder_rule_get_sources (EMVFolderRule *rule) { g_return_val_if_fail (rule != NULL, NULL); return &rule->priv->sources; } static gboolean check_queue_has_key (gpointer key, gpointer value, gpointer user_data) { EMVFolderRule *rule = user_data; g_return_val_if_fail (rule != NULL, FALSE); return g_queue_find (&rule->priv->sources, key) == NULL; } void em_vfolder_rule_sources_changed (EMVFolderRule *rule) { g_return_if_fail (rule != NULL); g_hash_table_foreach_remove ( rule->priv->include_subfolders, check_queue_has_key, rule); } gboolean em_vfolder_rule_source_get_include_subfolders (EMVFolderRule *rule, const gchar *source) { g_return_val_if_fail (rule != NULL, FALSE); g_return_val_if_fail (source != NULL, FALSE); source = em_vfolder_rule_find_source (rule, source); if (source == NULL) return FALSE; return g_hash_table_contains ( rule->priv->include_subfolders, source); } void em_vfolder_rule_source_set_include_subfolders (EMVFolderRule *rule, const gchar *source, gboolean include_subfolders) { g_return_if_fail (rule != NULL); g_return_if_fail (source != NULL); source = em_vfolder_rule_find_source (rule, source); g_return_if_fail (source != NULL); if (include_subfolders) g_hash_table_add ( rule->priv->include_subfolders, (gpointer) source); else g_hash_table_remove ( rule->priv->include_subfolders, (gpointer) source); } void em_vfolder_rule_set_with (EMVFolderRule *rule, em_vfolder_rule_with_t with) { g_return_if_fail (rule != NULL); rule->priv->with = with; } em_vfolder_rule_with_t em_vfolder_rule_get_with (EMVFolderRule *rule) { g_return_val_if_fail (rule != NULL, FALSE); return rule->priv->with; } void em_vfolder_rule_set_autoupdate (EMVFolderRule *rule, gboolean autoupdate) { g_return_if_fail (rule != NULL); rule->priv->autoupdate = autoupdate; } gboolean em_vfolder_rule_get_autoupdate (EMVFolderRule *rule) { g_return_val_if_fail (rule != NULL, EM_VFOLDER_RULE_WITH_SPECIFIC); return rule->priv->autoupdate; } static gint validate (EFilterRule *fr, EAlert **alert) { g_return_val_if_fail (fr != NULL, 0); g_warn_if_fail (alert == NULL || *alert == NULL); if (!fr->name || !*fr->name) { if (alert) *alert = e_alert_new ("mail:no-name-vfolder", NULL); return 0; } /* We have to have at least one source set in the "specific" case. * Do not translate this string! */ if (((EMVFolderRule *) fr)->priv->with == EM_VFOLDER_RULE_WITH_SPECIFIC && g_queue_is_empty (&((EMVFolderRule *) fr)->priv->sources)) { if (alert) *alert = e_alert_new ("mail:vfolder-no-source", NULL); return 0; } return E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->validate (fr, alert); } static gint queue_eq (GQueue *queue_a, GQueue *queue_b) { GList *link_a; GList *link_b; gint truth = TRUE; link_a = g_queue_peek_head_link (queue_a); link_b = g_queue_peek_head_link (queue_b); while (truth && link_a != NULL && link_b != NULL) { gchar *uri_a = link_a->data; gchar *uri_b = link_b->data; truth = (strcmp (uri_a, uri_b)== 0); link_a = g_list_next (link_a); link_b = g_list_next (link_b); } return truth && link_a == NULL && link_b == NULL; } static gint vfolder_eq (EFilterRule *fr, EFilterRule *cm) { return E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->eq (fr, cm) && queue_eq ( &((EMVFolderRule *) fr)->priv->sources, &((EMVFolderRule *) cm)->priv->sources); } static xmlNodePtr xml_encode (EFilterRule *fr) { EMVFolderRule *vr =(EMVFolderRule *) fr; xmlNodePtr node, set, work; GList *head, *link; node = E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->xml_encode (fr); g_return_val_if_fail (node != NULL, NULL); g_return_val_if_fail (vr->priv->with < G_N_ELEMENTS (with_names), NULL); set = xmlNewNode (NULL, (const guchar *)"sources"); xmlAddChild (node, set); xmlSetProp (set, (const guchar *)"with", (guchar *) with_names[vr->priv->with]); xmlSetProp (set, (const guchar *)"autoupdate", (guchar *) (vr->priv->autoupdate ? "true" : "false")); head = g_queue_peek_head_link (&vr->priv->sources); for (link = head; link != NULL; link = g_list_next (link)) { const gchar *uri = link->data; work = xmlNewNode (NULL, (const guchar *) "folder"); xmlSetProp (work, (const guchar *) "uri", (guchar *) uri); xmlSetProp (work, (const guchar *) "include-subfolders", (guchar *) (em_vfolder_rule_source_get_include_subfolders (vr, uri) ? "true" : "false")); xmlAddChild (set, work); } return node; } static void set_with (EMVFolderRule *vr, const gchar *name) { gint i; for (i = 0; i < G_N_ELEMENTS (with_names); i++) { if (!strcmp (name, with_names[i])) { vr->priv->with = i; return; } } vr->priv->with = 0; } static gint xml_decode (EFilterRule *fr, xmlNodePtr node, ERuleContext *f) { xmlNodePtr set, work; gint result; EMVFolderRule *vr =(EMVFolderRule *) fr; gchar *tmp; result = E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)-> xml_decode (fr, node, f); if (result != 0) return result; /* handle old format file, vfolder source is in filterrule */ if (strcmp (fr->source, "incoming")!= 0) { set_with (vr, fr->source); g_free (fr->source); fr->source = g_strdup ("incoming"); } set = node->children; while (set) { if (!strcmp ((gchar *) set->name, "sources")) { tmp = (gchar *) xmlGetProp (set, (const guchar *)"with"); if (tmp) { set_with (vr, tmp); xmlFree (tmp); } tmp = (gchar *) xmlGetProp (set, (const guchar *) "autoupdate"); if (tmp) { vr->priv->autoupdate = g_str_equal (tmp, "true"); xmlFree (tmp); } work = set->children; while (work) { if (!strcmp ((gchar *) work->name, "folder")) { tmp = (gchar *) xmlGetProp (work, (const guchar *)"uri"); if (tmp) { gchar *include_subfolders; g_queue_push_tail (&vr->priv->sources, g_strdup (tmp)); include_subfolders = (gchar *) xmlGetProp (work, (const guchar *) "include-subfolders"); if (include_subfolders) { em_vfolder_rule_source_set_include_subfolders ( vr, tmp, g_str_equal (include_subfolders, "true")); xmlFree (include_subfolders); } xmlFree (tmp); } } work = work->next; } } set = set->next; } return 0; } static void rule_copy (EFilterRule *dest, EFilterRule *src) { EMVFolderRule *vdest, *vsrc; GList *head, *link; gchar *uri; vdest =(EMVFolderRule *) dest; vsrc =(EMVFolderRule *) src; while ((uri = g_queue_pop_head (&vdest->priv->sources)) != NULL) g_free (uri); em_vfolder_rule_sources_changed (vdest); head = g_queue_peek_head_link (&vsrc->priv->sources); for (link = head; link != NULL; link = g_list_next (link)) { const gchar *uri = link->data; g_queue_push_tail (&vdest->priv->sources, g_strdup (uri)); em_vfolder_rule_source_set_include_subfolders ( vdest, uri, em_vfolder_rule_source_get_include_subfolders (vsrc, uri)); } vdest->priv->with = vsrc->priv->with; vdest->priv->autoupdate = vsrc->priv->autoupdate; E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)->copy (dest, src); } static GtkWidget * get_widget (EFilterRule *fr, ERuleContext *rc) { GtkWidget *widget; widget = E_FILTER_RULE_CLASS (em_vfolder_rule_parent_class)-> get_widget (fr, rc); return widget; }