/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 1999-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: Michael Zucchi */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "camel-db.h" #include "camel-debug.h" #include "camel-folder.h" #include "camel-store.h" #include "camel-vee-summary.h" #include "camel-vee-folder.h" #include "camel-vee-store.h" #include "camel-vtrash-folder.h" #include "camel-string-utils.h" #define d(x) #define CAMEL_VEE_SUMMARY_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_VEE_SUMMARY, CamelVeeSummaryPrivate)) struct _CamelVeeSummaryPrivate { /* CamelFolder * => GHashTable * of gchar *vuid */ GHashTable *vuids_by_subfolder; }; G_DEFINE_TYPE (CamelVeeSummary, camel_vee_summary, CAMEL_TYPE_FOLDER_SUMMARY) static void vee_message_info_free (CamelFolderSummary *s, CamelMessageInfo *info) { CamelVeeMessageInfo *mi = (CamelVeeMessageInfo *) info; g_object_unref (mi->orig_summary); CAMEL_FOLDER_SUMMARY_CLASS (camel_vee_summary_parent_class)->message_info_free (s, info); } static CamelMessageInfo * vee_message_info_clone (CamelFolderSummary *s, const CamelMessageInfo *mi) { CamelVeeMessageInfo *to; const CamelVeeMessageInfo *from = (const CamelVeeMessageInfo *) mi; to = (CamelVeeMessageInfo *) camel_message_info_new (s); to->orig_summary = g_object_ref (from->orig_summary); to->info.summary = s; to->info.uid = camel_pstring_strdup (from->info.uid); return (CamelMessageInfo *) to; } #define HANDLE_NULL_INFO(value) if (!rmi) { d(g_warning (G_STRLOC ": real info is NULL for %s, safeguarding\n", mi->uid)); return value; } static gconstpointer vee_info_ptr (const CamelMessageInfo *mi, gint id) { CamelVeeMessageInfo *vmi = (CamelVeeMessageInfo *) mi; CamelMessageInfo *rmi; gpointer p; rmi = camel_folder_summary_get (vmi->orig_summary, mi->uid + 8); HANDLE_NULL_INFO (NULL); p = (gpointer) camel_message_info_ptr (rmi, id); camel_message_info_unref (rmi); return p; } static guint32 vee_info_uint32 (const CamelMessageInfo *mi, gint id) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); guint32 ret; HANDLE_NULL_INFO (0); ret = camel_message_info_uint32 (rmi, id); camel_message_info_unref (rmi); return ret; } static time_t vee_info_time (const CamelMessageInfo *mi, gint id) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); time_t ret; HANDLE_NULL_INFO (0); ret = camel_message_info_time (rmi, id); camel_message_info_unref (rmi); return ret; } static gboolean vee_info_user_flag (const CamelMessageInfo *mi, const gchar *id) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); gboolean ret; HANDLE_NULL_INFO (FALSE); ret = camel_message_info_user_flag (rmi, id); camel_message_info_unref (rmi); return ret; } static const gchar * vee_info_user_tag (const CamelMessageInfo *mi, const gchar *id) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); const gchar *ret; HANDLE_NULL_INFO (""); ret = camel_message_info_user_tag (rmi, id); camel_message_info_unref (rmi); return ret; } static void vee_summary_notify_mi_changed (CamelVeeFolder *vfolder, CamelMessageInfo *mi) { CamelFolderChangeInfo *changes; g_return_if_fail (vfolder != NULL); g_return_if_fail (mi != NULL); changes = camel_folder_change_info_new (); camel_folder_change_info_change_uid (changes, camel_message_info_uid (mi)); camel_folder_changed (CAMEL_FOLDER (vfolder), changes); camel_folder_change_info_free (changes); } static gboolean vee_info_set_user_flag (CamelMessageInfo *mi, const gchar *name, gboolean value) { gint res = FALSE; CamelVeeFolder *vf = (CamelVeeFolder *) camel_folder_summary_get_folder (mi->summary); if (mi->uid) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); gboolean ignore_changes = !CAMEL_IS_VTRASH_FOLDER (vf); HANDLE_NULL_INFO (FALSE); /* ignore changes done in the folder itself, * unless it's a vTrash or vJunk folder */ if (ignore_changes) camel_vee_folder_ignore_next_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); res = camel_message_info_set_user_flag (rmi, name, value); if (ignore_changes) { if (res) vee_summary_notify_mi_changed (vf, mi); else camel_vee_folder_remove_from_ignore_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); } camel_message_info_unref (rmi); } return res; } static gboolean vee_info_set_user_tag (CamelMessageInfo *mi, const gchar *name, const gchar *value) { gint res = FALSE; if (mi->uid) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); CamelVeeFolder *vf = (CamelVeeFolder *) camel_folder_summary_get_folder (mi->summary); gboolean ignore_changes = !CAMEL_IS_VTRASH_FOLDER (vf); HANDLE_NULL_INFO (FALSE); /* ignore changes done in the folder itself, * unless it's a vTrash or vJunk folder */ if (ignore_changes) camel_vee_folder_ignore_next_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); res = camel_message_info_set_user_tag (rmi, name, value); if (ignore_changes) { if (res) vee_summary_notify_mi_changed (vf, mi); else camel_vee_folder_remove_from_ignore_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); } camel_message_info_unref (rmi); } return res; } static gboolean vee_info_set_flags (CamelMessageInfo *mi, guint32 flags, guint32 set) { gint res = FALSE; CamelVeeFolder *vf = CAMEL_VEE_FOLDER (camel_folder_summary_get_folder (mi->summary)); /* first update original message info... */ if (mi->uid) { CamelMessageInfo *rmi = camel_folder_summary_get (((CamelVeeMessageInfo *) mi)->orig_summary, mi->uid + 8); gboolean ignore_changes = !CAMEL_IS_VTRASH_FOLDER (vf); HANDLE_NULL_INFO (FALSE); /* ignore changes done in the folder itself, * unless it's a vTrash or vJunk folder */ if (ignore_changes) camel_vee_folder_ignore_next_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); res = camel_message_info_set_flags (rmi, flags, set); if (res) { /* update flags on itself too */ camel_folder_summary_replace_flags (mi->summary, mi); } if (ignore_changes) { if (res) vee_summary_notify_mi_changed (vf, mi); else camel_vee_folder_remove_from_ignore_changed_event (vf, camel_folder_summary_get_folder (rmi->summary)); } camel_message_info_unref (rmi); } /* Do not call parent class' info_set_flags, to not do flood * of change notifications, rather wait for a notification * from original folder, and propagate the change in counts * through camel_vee_summary_replace_flags(). */ /*if (res) CAMEL_FOLDER_SUMMARY_CLASS (camel_vee_summary_parent_class)->info_set_flags (mi, flags, set);*/ return res; } static CamelMessageInfo * message_info_from_uid (CamelFolderSummary *s, const gchar *uid) { CamelMessageInfo *info; info = camel_folder_summary_peek_loaded (s, uid); if (!info) { CamelVeeMessageInfo *vinfo; CamelFolder *orig_folder; /* This function isn't really nice. But no great way * But in vfolder case, this may not be so bad, as vuid has the hash in first 8 bytes. * So this just compares the entire string only if it belongs to the same folder. * Otherwise, the first byte itself would return in strcmp, saving the CPU. */ if (!camel_folder_summary_check_uid (s, uid)) { d ( g_message ("Unable to find %s in the summary of %s", uid, camel_folder_get_full_name (camel_folder_summary_get_folder (s->folder)))); return NULL; } /* Create the info and load it, its so easy. */ info = camel_message_info_new (s); info->dirty = FALSE; info->uid = camel_pstring_strdup (uid); orig_folder = camel_vee_folder_get_vee_uid_folder ( (CamelVeeFolder *) camel_folder_summary_get_folder (s), uid); g_return_val_if_fail (orig_folder != NULL, NULL); vinfo = (CamelVeeMessageInfo *) info; vinfo->orig_summary = orig_folder->summary; g_object_ref (vinfo->orig_summary); camel_message_info_ref (info); camel_folder_summary_insert (s, info, FALSE); } return info; } static void vee_summary_finalize (GObject *object) { CamelVeeSummaryPrivate *priv; priv = CAMEL_VEE_SUMMARY_GET_PRIVATE (object); g_hash_table_destroy (priv->vuids_by_subfolder); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (camel_vee_summary_parent_class)->finalize (object); } static void camel_vee_summary_class_init (CamelVeeSummaryClass *class) { GObjectClass *object_class; CamelFolderSummaryClass *folder_summary_class; g_type_class_add_private (class, sizeof (CamelVeeSummaryPrivate)); object_class = G_OBJECT_CLASS (class); object_class->finalize = vee_summary_finalize; folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (class); folder_summary_class->message_info_size = sizeof (CamelVeeMessageInfo); folder_summary_class->content_info_size = 0; folder_summary_class->message_info_clone = vee_message_info_clone; folder_summary_class->message_info_free = vee_message_info_free; folder_summary_class->info_ptr = vee_info_ptr; folder_summary_class->info_uint32 = vee_info_uint32; folder_summary_class->info_time = vee_info_time; folder_summary_class->info_user_flag = vee_info_user_flag; folder_summary_class->info_user_tag = vee_info_user_tag; folder_summary_class->info_set_user_flag = vee_info_set_user_flag; folder_summary_class->info_set_user_tag = vee_info_set_user_tag; folder_summary_class->info_set_flags = vee_info_set_flags; folder_summary_class->message_info_from_uid = message_info_from_uid; } static void camel_vee_summary_init (CamelVeeSummary *vee_summary) { vee_summary->priv = CAMEL_VEE_SUMMARY_GET_PRIVATE (vee_summary); vee_summary->priv->vuids_by_subfolder = g_hash_table_new_full ( (GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal, (GDestroyNotify) NULL, (GDestroyNotify) g_hash_table_destroy); } /** * camel_vee_summary_new: * @parent: Folder its attached to. * * This will create a new CamelVeeSummary object and read in the * summary data from disk, if it exists. * * Returns: A new CamelVeeSummary object. **/ CamelFolderSummary * camel_vee_summary_new (CamelFolder *parent) { CamelFolderSummary *summary; CamelStore *parent_store; const gchar *full_name; summary = g_object_new (CAMEL_TYPE_VEE_SUMMARY, "folder", parent, NULL); summary->flags |= CAMEL_FOLDER_SUMMARY_IN_MEMORY_ONLY; /* not using DB for vee folder summaries, drop the table */ full_name = camel_folder_get_full_name (parent); parent_store = camel_folder_get_parent_store (parent); camel_db_delete_folder (parent_store->cdb_w, full_name, NULL); return summary; } static void get_uids_for_subfolder (gpointer key, gpointer value, gpointer user_data) { g_hash_table_insert (user_data, (gpointer) camel_pstring_strdup (key), GINT_TO_POINTER (1)); } /** * camel_vee_summary_get_uids_for_subfolder: * * FIXME Document me! * * Since: 3.6 **/ GHashTable * camel_vee_summary_get_uids_for_subfolder (CamelVeeSummary *summary, CamelFolder *subfolder) { GHashTable *vuids, *known_uids; g_return_val_if_fail (CAMEL_IS_VEE_SUMMARY (summary), NULL); g_return_val_if_fail (CAMEL_IS_FOLDER (subfolder), NULL); camel_folder_summary_lock (&summary->summary); /* uses direct hash, because strings are supposed to be from the string pool */ known_uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); vuids = g_hash_table_lookup (summary->priv->vuids_by_subfolder, subfolder); if (vuids) { g_hash_table_foreach (vuids, get_uids_for_subfolder, known_uids); } camel_folder_summary_unlock (&summary->summary); return known_uids; } /* unref returned pointer with camel_message_info_unref() */ CamelVeeMessageInfo * camel_vee_summary_add (CamelVeeSummary *s, CamelVeeMessageInfoData *mi_data) { CamelVeeMessageInfo *vmi; const gchar *vuid; CamelVeeSubfolderData *sf_data; CamelFolder *orig_folder; GHashTable *vuids; g_return_val_if_fail (CAMEL_IS_VEE_SUMMARY (s), NULL); g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (mi_data), NULL); camel_folder_summary_lock (&s->summary); sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); orig_folder = camel_vee_subfolder_data_get_folder (sf_data); vmi = (CamelVeeMessageInfo *) camel_folder_summary_peek_loaded (&s->summary, vuid); if (vmi) { /* Possible that the entry is loaded, see if it has the summary */ d (g_message ("%s - already there\n", vuid)); if (!vmi->orig_summary) vmi->orig_summary = g_object_ref (orig_folder->summary); camel_folder_summary_unlock (&s->summary); return vmi; } vmi = (CamelVeeMessageInfo *) camel_message_info_new (&s->summary); vmi->orig_summary = g_object_ref (orig_folder->summary); vmi->info.uid = (gchar *) camel_pstring_strdup (vuid); camel_message_info_ref (vmi); vuids = g_hash_table_lookup (s->priv->vuids_by_subfolder, orig_folder); if (vuids) { g_hash_table_insert (vuids, (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (1)); } else { vuids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL); g_hash_table_insert (vuids, (gpointer) camel_pstring_strdup (vuid), GINT_TO_POINTER (1)); g_hash_table_insert (s->priv->vuids_by_subfolder, orig_folder, vuids); } camel_folder_summary_insert (&s->summary, (CamelMessageInfo *) vmi, FALSE); camel_folder_summary_unlock (&s->summary); return vmi; } /** * camel_vee_summary_remove: * * FIXME Document me! * * Since: 3.6 **/ void camel_vee_summary_remove (CamelVeeSummary *summary, const gchar *vuid, CamelFolder *subfolder) { CamelMessageInfo *mi; GHashTable *vuids; g_return_if_fail (CAMEL_IS_VEE_SUMMARY (summary)); g_return_if_fail (vuid != NULL); g_return_if_fail (subfolder != NULL); camel_folder_summary_lock (&summary->summary); vuids = g_hash_table_lookup (summary->priv->vuids_by_subfolder, subfolder); if (vuids) { g_hash_table_remove (vuids, vuid); if (!g_hash_table_size (vuids)) g_hash_table_remove (summary->priv->vuids_by_subfolder, subfolder); } mi = camel_folder_summary_peek_loaded (&summary->summary, vuid); camel_folder_summary_remove_uid (&summary->summary, vuid); if (mi) { /* under twice, the first for camel_folder_summary_peek_loaded(), * the second to actually free the mi */ camel_message_info_unref (mi); camel_message_info_unref (mi); } camel_folder_summary_unlock (&summary->summary); } /** * camel_vee_summary_replace_flags: * @summary: a #CamelVeeSummary * @uid: a message UID to update flags for * * Makes sure @summary flags on @uid corresponds to those * in the subfolder of vee-folder, and updates internal counts * on @summary as well. * * Since: 3.6 **/ void camel_vee_summary_replace_flags (CamelVeeSummary *summary, const gchar *uid) { CamelMessageInfo *mi; g_return_if_fail (CAMEL_IS_VEE_SUMMARY (summary)); g_return_if_fail (uid != NULL); camel_folder_summary_lock (&summary->summary); mi = camel_folder_summary_get (&summary->summary, uid); if (!mi) { camel_folder_summary_unlock (&summary->summary); return; } camel_folder_summary_replace_flags (&summary->summary, mi); camel_message_info_unref (mi); camel_folder_summary_unlock (&summary->summary); }