/* * Copyright (C) 2012 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 . * * Authors: Milan Crha */ #ifdef HAVE_CONFIG_H #include #endif #include "camel-string-utils.h" #include "camel-store.h" #include "camel-vee-data-cache.h" #define CAMEL_VEE_SUBFOLDER_DATA_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_VEE_SUBFOLDER_DATA, CamelVeeSubfolderDataPrivate)) struct _CamelVeeSubfolderDataPrivate { CamelFolder *folder; const gchar *folder_id; /* stored in string pool */ }; G_DEFINE_TYPE ( CamelVeeSubfolderData, camel_vee_subfolder_data, G_TYPE_OBJECT) static void vee_subfolder_data_dispose (GObject *object) { CamelVeeSubfolderDataPrivate *priv; priv = CAMEL_VEE_SUBFOLDER_DATA_GET_PRIVATE (object); g_clear_object (&priv->folder); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (camel_vee_subfolder_data_parent_class)-> dispose (object); } static void vee_subfolder_data_finalize (GObject *object) { CamelVeeSubfolderDataPrivate *priv; priv = CAMEL_VEE_SUBFOLDER_DATA_GET_PRIVATE (object); if (priv->folder_id != NULL) camel_pstring_free (priv->folder_id); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (camel_vee_subfolder_data_parent_class)-> finalize (object); } static void camel_vee_subfolder_data_class_init (CamelVeeSubfolderDataClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (CamelVeeSubfolderDataPrivate)); object_class = G_OBJECT_CLASS (class); object_class->dispose = vee_subfolder_data_dispose; object_class->finalize = vee_subfolder_data_finalize; } static void camel_vee_subfolder_data_init (CamelVeeSubfolderData *data) { data->priv = CAMEL_VEE_SUBFOLDER_DATA_GET_PRIVATE (data); } static void vee_subfolder_data_hash_folder (CamelFolder *folder, gchar buffer[8]) { CamelStore *parent_store; GChecksum *checksum; guint8 *digest; gsize length; gint state = 0, save = 0; gchar *ptr_string; const gchar *uid; gint i; length = g_checksum_type_get_length (G_CHECKSUM_MD5); digest = g_alloca (length); checksum = g_checksum_new (G_CHECKSUM_MD5); parent_store = camel_folder_get_parent_store (folder); uid = camel_service_get_uid (CAMEL_SERVICE (parent_store)); g_checksum_update (checksum, (guchar *) uid, -1); ptr_string = g_strdup_printf ("%p", folder); g_checksum_update (checksum, (guchar *) ptr_string, -1); g_free (ptr_string); g_checksum_get_digest (checksum, digest, &length); g_checksum_free (checksum); g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save); g_base64_encode_close (FALSE, buffer, &state, &save); for (i = 0; i < 8; i++) { if (buffer[i] == '+') buffer[i] = '.'; if (buffer[i] == '/') buffer[i] = '_'; } } /** * camel_vee_subfolder_data_new: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeSubfolderData * camel_vee_subfolder_data_new (CamelFolder *folder) { CamelVeeSubfolderData *data; gchar buffer[8], *folder_id; g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); data = g_object_new (CAMEL_TYPE_VEE_SUBFOLDER_DATA, NULL); data->priv->folder = g_object_ref (folder); vee_subfolder_data_hash_folder (folder, buffer); folder_id = g_strndup (buffer, 8); data->priv->folder_id = camel_pstring_add (folder_id, TRUE); return data; } /** * camel_vee_subfolder_data_get_folder: * * FIXME Document me! * * Since: 3.6 **/ CamelFolder * camel_vee_subfolder_data_get_folder (CamelVeeSubfolderData *data) { g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL); return data->priv->folder; } /** * camel_vee_subfolder_data_get_folder_id: * * FIXME Document me! * * Since: 3.6 **/ const gchar * camel_vee_subfolder_data_get_folder_id (CamelVeeSubfolderData *data) { g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL); return data->priv->folder_id; } /* ----------------------------------------------------------------------- */ #define CAMEL_VEE_MESSAGE_INFO_DATA_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, CamelVeeMessageInfoDataPrivate)) struct _CamelVeeMessageInfoDataPrivate { CamelVeeSubfolderData *subfolder_data; const gchar *orig_message_uid; /* stored in string pool */ const gchar *vee_message_uid; /* stored in string pool */ }; G_DEFINE_TYPE ( CamelVeeMessageInfoData, camel_vee_message_info_data, G_TYPE_OBJECT) static void vee_message_info_data_dispose (GObject *object) { CamelVeeMessageInfoDataPrivate *priv; priv = CAMEL_VEE_MESSAGE_INFO_DATA_GET_PRIVATE (object); g_clear_object (&priv->subfolder_data); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (camel_vee_message_info_data_parent_class)-> dispose (object); } static void vee_message_info_data_finalize (GObject *object) { CamelVeeMessageInfoDataPrivate *priv; priv = CAMEL_VEE_MESSAGE_INFO_DATA_GET_PRIVATE (object); if (priv->orig_message_uid != NULL) camel_pstring_free (priv->orig_message_uid); if (priv->vee_message_uid != NULL) camel_pstring_free (priv->vee_message_uid); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (camel_vee_message_info_data_parent_class)-> finalize (object); } static void camel_vee_message_info_data_class_init (CamelVeeMessageInfoDataClass *class) { GObjectClass *object_class; g_type_class_add_private ( class, sizeof (CamelVeeMessageInfoDataPrivate)); object_class = G_OBJECT_CLASS (class); object_class->dispose = vee_message_info_data_dispose; object_class->finalize = vee_message_info_data_finalize; } static void camel_vee_message_info_data_init (CamelVeeMessageInfoData *data) { data->priv = CAMEL_VEE_MESSAGE_INFO_DATA_GET_PRIVATE (data); } /** * camel_vee_message_info_data_new: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeMessageInfoData * camel_vee_message_info_data_new (CamelVeeSubfolderData *subfolder_data, const gchar *orig_message_uid) { CamelVeeMessageInfoData *data; gchar *vee_message_uid; g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (subfolder_data), NULL); g_return_val_if_fail (orig_message_uid != NULL, NULL); data = g_object_new (CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, NULL); data->priv->subfolder_data = g_object_ref (subfolder_data); vee_message_uid = g_strconcat (camel_vee_subfolder_data_get_folder_id (subfolder_data), orig_message_uid, NULL); data->priv->orig_message_uid = camel_pstring_strdup (orig_message_uid); data->priv->vee_message_uid = camel_pstring_add (vee_message_uid, TRUE); return data; } /** * camel_vee_message_info_data_get_subfolder_data: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeSubfolderData * camel_vee_message_info_data_get_subfolder_data (CamelVeeMessageInfoData *data) { g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL); return data->priv->subfolder_data; } /** * camel_vee_message_info_data_get_orig_message_uid: * * FIXME Document me! * * Since: 3.6 **/ const gchar * camel_vee_message_info_data_get_orig_message_uid (CamelVeeMessageInfoData *data) { g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL); return data->priv->orig_message_uid; } /** * camel_vee_message_info_data_get_vee_message_uid: * * FIXME Document me! * * Since: 3.6 **/ const gchar * camel_vee_message_info_data_get_vee_message_uid (CamelVeeMessageInfoData *data) { g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL); return data->priv->vee_message_uid; } /* ----------------------------------------------------------------------- */ #define CAMEL_VEE_DATA_CACHE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_VEE_DATA_CACHE, CamelVeeDataCachePrivate)) struct _CamelVeeDataCachePrivate { GMutex sf_mutex; /* guards subfolder_hash */ GHashTable *subfolder_hash; /* CamelFolder * => CamelVeeSubfolderData * */ GMutex mi_mutex; /* guards message_info_hash */ GHashTable *orig_message_uid_hash; /* VeeData * => CamelVeeMessageInfoData * */ GHashTable *vee_message_uid_hash; /* const gchar *vee_uid => CamelVeeMessageInfoData * */ }; G_DEFINE_TYPE (CamelVeeDataCache, camel_vee_data_cache, G_TYPE_OBJECT) typedef struct _VeeData { CamelFolder *folder; const gchar *orig_message_uid; } VeeData; static guint vee_data_hash (gconstpointer ptr) { const VeeData *vee_data = ptr; if (!vee_data) return 0; return g_direct_hash (vee_data->folder) + g_str_hash (vee_data->orig_message_uid); } static gboolean vee_data_equal (gconstpointer v1, gconstpointer v2) { const VeeData *vee_data1 = v1, *vee_data2 = v2; if (!v1 || !v2) return v1 == v2; /* can contain ponters directly, strings are always from the string pool */ return v1 == v2 || (vee_data1->folder == vee_data2->folder && vee_data1->orig_message_uid == vee_data2->orig_message_uid); } static void vee_data_cache_dispose (GObject *object) { CamelVeeDataCachePrivate *priv; priv = CAMEL_VEE_DATA_CACHE_GET_PRIVATE (object); if (priv->subfolder_hash != NULL) { g_hash_table_destroy (priv->subfolder_hash); priv->subfolder_hash = NULL; } if (priv->orig_message_uid_hash != NULL) { g_hash_table_destroy (priv->orig_message_uid_hash); priv->orig_message_uid_hash = NULL; } if (priv->vee_message_uid_hash != NULL) { g_hash_table_destroy (priv->vee_message_uid_hash); priv->vee_message_uid_hash = NULL; } /* Chain up to parent's dispose () method. */ G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->dispose (object); } static void vee_data_cache_finalize (GObject *object) { CamelVeeDataCachePrivate *priv; priv = CAMEL_VEE_DATA_CACHE_GET_PRIVATE (object); g_mutex_clear (&priv->sf_mutex); g_mutex_clear (&priv->mi_mutex); /* Chain up to parent's finalize () method. */ G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->finalize (object); } static void camel_vee_data_cache_class_init (CamelVeeDataCacheClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (CamelVeeDataCachePrivate)); object_class = G_OBJECT_CLASS (class); object_class->dispose = vee_data_cache_dispose; object_class->finalize = vee_data_cache_finalize; } static void camel_vee_data_cache_init (CamelVeeDataCache *data_cache) { data_cache->priv = CAMEL_VEE_DATA_CACHE_GET_PRIVATE (data_cache); g_mutex_init (&data_cache->priv->sf_mutex); data_cache->priv->subfolder_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); g_mutex_init (&data_cache->priv->mi_mutex); data_cache->priv->orig_message_uid_hash = g_hash_table_new_full (vee_data_hash, vee_data_equal, g_free, g_object_unref); data_cache->priv->vee_message_uid_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); } /** * camel_vee_data_cache_new: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeDataCache * camel_vee_data_cache_new (void) { return g_object_new (CAMEL_TYPE_VEE_DATA_CACHE, NULL); } /** * camel_vee_data_cache_add_subfolder: * * FIXME Document me! * * Since: 3.6 **/ void camel_vee_data_cache_add_subfolder (CamelVeeDataCache *data_cache, CamelFolder *subfolder) { CamelVeeSubfolderData *sf_data; g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); g_mutex_lock (&data_cache->priv->mi_mutex); g_mutex_lock (&data_cache->priv->sf_mutex); sf_data = g_hash_table_lookup (data_cache->priv->subfolder_hash, subfolder); if (!sf_data) { GPtrArray *uids; gint ii; sf_data = camel_vee_subfolder_data_new (subfolder); g_hash_table_insert (data_cache->priv->subfolder_hash, subfolder, sf_data); /* camel_vee_data_cache_get_message_info_data() caches uids on demand, * while here are cached all known uids in once - it is better when * the folder is used in Unmatched folder, where the uid/vuid will * be used in the vfolder or Unmatched folder anyway */ uids = camel_folder_get_uids (subfolder); if (uids) { for (ii = 0; ii < uids->len; ii++) { VeeData vdata; CamelVeeMessageInfoData *mi_data; /* make sure the orig_message_uid comes from the string pool */ vdata.folder = subfolder; vdata.orig_message_uid = camel_pstring_strdup (uids->pdata[ii]); mi_data = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata); if (!mi_data) { VeeData *hash_data; mi_data = camel_vee_message_info_data_new (sf_data, vdata.orig_message_uid); hash_data = g_new0 (VeeData, 1); hash_data->folder = subfolder; hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data); g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, mi_data); g_hash_table_insert ( data_cache->priv->vee_message_uid_hash, (gpointer) camel_vee_message_info_data_get_vee_message_uid (mi_data), mi_data); } camel_pstring_free (vdata.orig_message_uid); } camel_folder_free_uids (subfolder, uids); } } g_mutex_unlock (&data_cache->priv->sf_mutex); g_mutex_unlock (&data_cache->priv->mi_mutex); } static gboolean remove_vee_by_folder_cb (gpointer key, gpointer value, gpointer user_data) { CamelVeeMessageInfoData *mi_data = value; CamelVeeSubfolderData *sf_data; CamelFolder *folder = user_data; if (!mi_data) return FALSE; sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); return sf_data && camel_vee_subfolder_data_get_folder (sf_data) == folder; } static gboolean remove_orig_by_folder_cb (gpointer key, gpointer value, gpointer user_data) { VeeData *vee_data = key; CamelFolder *folder = user_data; return vee_data && vee_data->folder == folder; } /** * camel_vee_data_cache_remove_subfolder: * * FIXME Document me! * * Since: 3.6 **/ void camel_vee_data_cache_remove_subfolder (CamelVeeDataCache *data_cache, CamelFolder *subfolder) { g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); g_return_if_fail (CAMEL_IS_FOLDER (subfolder)); g_mutex_lock (&data_cache->priv->mi_mutex); g_mutex_lock (&data_cache->priv->sf_mutex); g_hash_table_foreach_remove (data_cache->priv->vee_message_uid_hash, remove_vee_by_folder_cb, subfolder); g_hash_table_foreach_remove (data_cache->priv->orig_message_uid_hash, remove_orig_by_folder_cb, subfolder); g_hash_table_remove (data_cache->priv->subfolder_hash, subfolder); g_mutex_unlock (&data_cache->priv->sf_mutex); g_mutex_unlock (&data_cache->priv->mi_mutex); } /** * camel_vee_data_cache_get_subfolder_data: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeSubfolderData * camel_vee_data_cache_get_subfolder_data (CamelVeeDataCache *data_cache, CamelFolder *folder) { CamelVeeSubfolderData *res; g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL); g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); g_mutex_lock (&data_cache->priv->sf_mutex); res = g_hash_table_lookup (data_cache->priv->subfolder_hash, folder); if (!res) { res = camel_vee_subfolder_data_new (folder); g_hash_table_insert (data_cache->priv->subfolder_hash, folder, res); } g_object_ref (res); g_mutex_unlock (&data_cache->priv->sf_mutex); return res; } /** * camel_vee_data_cache_contains_message_info_data: * * Returns whether data_cache contains certain UID for certain folder; * instead of camel_vee_data_cache_get_message_info_data() only * returns FALSE if not, while camel_vee_data_cache_get_message_info_data() * auto-adds it to data_cache. * * Since: 3.6 */ gboolean camel_vee_data_cache_contains_message_info_data (CamelVeeDataCache *data_cache, CamelFolder *folder, const gchar *orig_message_uid) { gboolean res; VeeData vdata; g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), FALSE); g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE); g_return_val_if_fail (orig_message_uid != NULL, FALSE); g_mutex_lock (&data_cache->priv->mi_mutex); /* make sure the orig_message_uid comes from the string pool */ vdata.folder = folder; vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid); res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata) != NULL; camel_pstring_free (vdata.orig_message_uid); g_mutex_unlock (&data_cache->priv->mi_mutex); return res; } /** * camel_vee_data_cache_get_message_info_data: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeMessageInfoData * camel_vee_data_cache_get_message_info_data (CamelVeeDataCache *data_cache, CamelFolder *folder, const gchar *orig_message_uid) { CamelVeeMessageInfoData *res; VeeData vdata; g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL); g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); g_return_val_if_fail (orig_message_uid != NULL, NULL); g_mutex_lock (&data_cache->priv->mi_mutex); /* make sure the orig_message_uid comes from the string pool */ vdata.folder = folder; vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid); res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata); if (!res) { VeeData *hash_data; CamelVeeSubfolderData *sf_data; /* this locks also priv->sf_mutex */ sf_data = camel_vee_data_cache_get_subfolder_data (data_cache, folder); if (!sf_data) { camel_pstring_free (vdata.orig_message_uid); g_mutex_unlock (&data_cache->priv->mi_mutex); g_return_val_if_fail (sf_data != NULL, NULL); } res = camel_vee_message_info_data_new (sf_data, orig_message_uid); /* res holds the reference now */ g_object_unref (sf_data); hash_data = g_new0 (VeeData, 1); hash_data->folder = folder; hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (res); g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, res); g_hash_table_insert ( data_cache->priv->vee_message_uid_hash, (gpointer) camel_vee_message_info_data_get_vee_message_uid (res), res); } camel_pstring_free (vdata.orig_message_uid); g_object_ref (res); g_mutex_unlock (&data_cache->priv->mi_mutex); return res; } /** * camel_vee_data_cache_get_message_info_data_by_vuid: * * FIXME Document me! * * Since: 3.6 **/ CamelVeeMessageInfoData * camel_vee_data_cache_get_message_info_data_by_vuid (CamelVeeDataCache *data_cache, const gchar *vee_message_uid) { CamelVeeMessageInfoData *res; const gchar *vuid; g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL); g_return_val_if_fail (vee_message_uid != NULL, NULL); g_mutex_lock (&data_cache->priv->mi_mutex); /* make sure vee_message_uid comes from the string pool */ vuid = camel_pstring_strdup (vee_message_uid); res = g_hash_table_lookup (data_cache->priv->vee_message_uid_hash, vuid); if (res) g_object_ref (res); g_mutex_unlock (&data_cache->priv->mi_mutex); camel_pstring_free (vuid); return res; } struct ForeachMiData { CamelFolder *fromfolder; void (* func) (CamelVeeMessageInfoData *mi_data, CamelFolder *subfolder, gpointer user_data); gpointer user_data; }; static void cvdc_foreach_mi_data_cb (gpointer key, gpointer value, gpointer user_data) { VeeData *vdata = key; CamelVeeMessageInfoData *mi_data = value; struct ForeachMiData *fmd = user_data; g_return_if_fail (key != NULL); g_return_if_fail (value != NULL); g_return_if_fail (user_data != NULL); if (!fmd->fromfolder || fmd->fromfolder == vdata->folder) fmd->func (mi_data, vdata->folder, fmd->user_data); } /** * camel_vee_data_cache_foreach_message_info_data: * * FIXME Document me! * * Since: 3.6 **/ void camel_vee_data_cache_foreach_message_info_data (CamelVeeDataCache *data_cache, CamelFolder *fromfolder, void (* func) (CamelVeeMessageInfoData *mi_data, CamelFolder *subfolder, gpointer user_data), gpointer user_data) { struct ForeachMiData fmd; g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); g_return_if_fail (func != NULL); g_mutex_lock (&data_cache->priv->mi_mutex); fmd.fromfolder = fromfolder; fmd.func = func; fmd.user_data = user_data; g_hash_table_foreach (data_cache->priv->orig_message_uid_hash, cvdc_foreach_mi_data_cb, &fmd); g_mutex_unlock (&data_cache->priv->mi_mutex); } /** * camel_vee_data_cache_remove_message_info_data: * * FIXME Document me! * * Since: 3.6 **/ void camel_vee_data_cache_remove_message_info_data (CamelVeeDataCache *data_cache, CamelVeeMessageInfoData *mi_data) { VeeData vdata; CamelVeeSubfolderData *sf_data; const gchar *vuid; g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache)); g_return_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (mi_data)); g_mutex_lock (&data_cache->priv->mi_mutex); g_object_ref (mi_data); sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data); vdata.folder = camel_vee_subfolder_data_get_folder (sf_data); vdata.orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data); vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data); g_hash_table_remove (data_cache->priv->vee_message_uid_hash, vuid); g_hash_table_remove (data_cache->priv->orig_message_uid_hash, &vdata); g_object_unref (mi_data); g_mutex_unlock (&data_cache->priv->mi_mutex); }