2 * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
4 * Authors: Milan Crha <mcrha@redhat.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "camel-string-utils.h"
26 #include "camel-store.h"
28 #include "camel-vee-data-cache.h"
30 struct _CamelVeeSubfolderDataPrivate {
32 const gchar *folder_id; /* stored in string pool */
35 G_DEFINE_TYPE (CamelVeeSubfolderData, camel_vee_subfolder_data, G_TYPE_OBJECT)
38 camel_vee_subfolder_data_dispose (GObject *object)
40 CamelVeeSubfolderData *data;
42 data = CAMEL_VEE_SUBFOLDER_DATA (object);
44 if (data->priv->folder)
45 g_object_unref (data->priv->folder);
46 data->priv->folder = NULL;
48 if (data->priv->folder_id)
49 camel_pstring_free (data->priv->folder_id);
50 data->priv->folder_id = NULL;
53 /* Chain up to parent's dispose () method. */
54 G_OBJECT_CLASS (camel_vee_subfolder_data_parent_class)->dispose (object);
58 camel_vee_subfolder_data_class_init (CamelVeeSubfolderDataClass *class)
60 GObjectClass *object_class;
62 g_type_class_add_private (class, sizeof (CamelVeeSubfolderDataPrivate));
64 object_class = G_OBJECT_CLASS (class);
65 object_class->dispose = camel_vee_subfolder_data_dispose;
69 camel_vee_subfolder_data_init (CamelVeeSubfolderData *data)
71 data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, CAMEL_TYPE_VEE_SUBFOLDER_DATA, CamelVeeSubfolderDataPrivate);
75 vee_subfolder_data_hash_folder (CamelFolder *folder,
78 CamelStore *parent_store;
82 gint state = 0, save = 0;
87 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
88 digest = g_alloca (length);
90 checksum = g_checksum_new (G_CHECKSUM_MD5);
91 parent_store = camel_folder_get_parent_store (folder);
92 uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
93 g_checksum_update (checksum, (guchar *) uid, -1);
95 ptr_string = g_strdup_printf ("%p", folder);
96 g_checksum_update (checksum, (guchar *) ptr_string, -1);
99 g_checksum_get_digest (checksum, digest, &length);
100 g_checksum_free (checksum);
102 g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save);
103 g_base64_encode_close (FALSE, buffer, &state, &save);
105 for (i = 0; i < 8; i++) {
106 if (buffer[i] == '+')
108 if (buffer[i] == '/')
114 * camel_vee_subfolder_data_new:
120 CamelVeeSubfolderData *
121 camel_vee_subfolder_data_new (CamelFolder *folder)
123 CamelVeeSubfolderData *data;
124 gchar buffer[8], *folder_id;
126 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
128 data = g_object_new (CAMEL_TYPE_VEE_SUBFOLDER_DATA, NULL);
129 data->priv->folder = g_object_ref (folder);
131 vee_subfolder_data_hash_folder (folder, buffer);
132 folder_id = g_strndup (buffer, 8);
134 data->priv->folder_id = camel_pstring_add (folder_id, TRUE);
140 * camel_vee_subfolder_data_get_folder:
147 camel_vee_subfolder_data_get_folder (CamelVeeSubfolderData *data)
149 g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL);
151 return data->priv->folder;
155 * camel_vee_subfolder_data_get_folder_id:
162 camel_vee_subfolder_data_get_folder_id (CamelVeeSubfolderData *data)
164 g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL);
166 return data->priv->folder_id;
169 /* ----------------------------------------------------------------------- */
171 struct _CamelVeeMessageInfoDataPrivate {
172 CamelVeeSubfolderData *subfolder_data;
173 const gchar *orig_message_uid; /* stored in string pool */
174 const gchar *vee_message_uid; /* stored in string pool */
177 G_DEFINE_TYPE (CamelVeeMessageInfoData, camel_vee_message_info_data, G_TYPE_OBJECT)
180 camel_vee_message_info_data_dispose (GObject *object)
182 CamelVeeMessageInfoData *data;
184 data = CAMEL_VEE_MESSAGE_INFO_DATA (object);
186 if (data->priv->subfolder_data)
187 g_object_unref (data->priv->subfolder_data);
188 data->priv->subfolder_data = NULL;
190 if (data->priv->orig_message_uid)
191 camel_pstring_free (data->priv->orig_message_uid);
192 data->priv->orig_message_uid = NULL;
194 if (data->priv->vee_message_uid)
195 camel_pstring_free (data->priv->vee_message_uid);
196 data->priv->vee_message_uid = NULL;
199 /* Chain up to parent's dispose () method. */
200 G_OBJECT_CLASS (camel_vee_message_info_data_parent_class)->dispose (object);
204 camel_vee_message_info_data_class_init (CamelVeeMessageInfoDataClass *class)
206 GObjectClass *object_class;
208 g_type_class_add_private (class, sizeof (CamelVeeMessageInfoDataPrivate));
210 object_class = G_OBJECT_CLASS (class);
211 object_class->dispose = camel_vee_message_info_data_dispose;
215 camel_vee_message_info_data_init (CamelVeeMessageInfoData *data)
217 data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, CamelVeeMessageInfoDataPrivate);
221 * camel_vee_message_info_data_new:
227 CamelVeeMessageInfoData *
228 camel_vee_message_info_data_new (CamelVeeSubfolderData *subfolder_data,
229 const gchar *orig_message_uid)
231 CamelVeeMessageInfoData *data;
232 gchar *vee_message_uid;
234 g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (subfolder_data), NULL);
235 g_return_val_if_fail (orig_message_uid != NULL, NULL);
237 data = g_object_new (CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, NULL);
238 data->priv->subfolder_data = g_object_ref (subfolder_data);
240 vee_message_uid = g_strconcat (camel_vee_subfolder_data_get_folder_id (subfolder_data), orig_message_uid, NULL);
242 data->priv->orig_message_uid = camel_pstring_strdup (orig_message_uid);
243 data->priv->vee_message_uid = camel_pstring_add (vee_message_uid, TRUE);
249 * camel_vee_message_info_data_get_subfolder_data:
255 CamelVeeSubfolderData *
256 camel_vee_message_info_data_get_subfolder_data (CamelVeeMessageInfoData *data)
258 g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL);
260 return data->priv->subfolder_data;
264 * camel_vee_message_info_data_get_orig_message_uid:
271 camel_vee_message_info_data_get_orig_message_uid (CamelVeeMessageInfoData *data)
273 g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL);
275 return data->priv->orig_message_uid;
279 * camel_vee_message_info_data_get_vee_message_uid:
286 camel_vee_message_info_data_get_vee_message_uid (CamelVeeMessageInfoData *data)
288 g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL);
290 return data->priv->vee_message_uid;
293 /* ----------------------------------------------------------------------- */
295 struct _CamelVeeDataCachePrivate {
296 GMutex sf_mutex; /* guards subfolder_hash */
297 GHashTable *subfolder_hash; /* CamelFolder * => CamelVeeSubfolderData * */
299 GMutex mi_mutex; /* guards message_info_hash */
300 GHashTable *orig_message_uid_hash; /* VeeData * => CamelVeeMessageInfoData * */
301 GHashTable *vee_message_uid_hash; /* const gchar *vee_uid => CamelVeeMessageInfoData * */
304 G_DEFINE_TYPE (CamelVeeDataCache, camel_vee_data_cache, G_TYPE_OBJECT)
306 typedef struct _VeeData {
308 const gchar *orig_message_uid;
312 vee_data_hash (gconstpointer ptr)
314 const VeeData *vee_data = ptr;
319 return g_direct_hash (vee_data->folder)
320 + g_str_hash (vee_data->orig_message_uid);
324 vee_data_equal (gconstpointer v1,
327 const VeeData *vee_data1 = v1, *vee_data2 = v2;
332 /* can contain ponters directly, strings are always from the string pool */
334 (vee_data1->folder == vee_data2->folder &&
335 vee_data1->orig_message_uid == vee_data2->orig_message_uid);
339 camel_vee_data_cache_dispose (GObject *object)
341 CamelVeeDataCache *data_cache;
343 data_cache = CAMEL_VEE_DATA_CACHE (object);
344 if (data_cache->priv) {
345 if (data_cache->priv->subfolder_hash)
346 g_hash_table_destroy (data_cache->priv->subfolder_hash);
347 data_cache->priv->subfolder_hash = NULL;
349 if (data_cache->priv->orig_message_uid_hash)
350 g_hash_table_destroy (data_cache->priv->orig_message_uid_hash);
351 data_cache->priv->orig_message_uid_hash = NULL;
353 if (data_cache->priv->vee_message_uid_hash)
354 g_hash_table_destroy (data_cache->priv->vee_message_uid_hash);
355 data_cache->priv->vee_message_uid_hash = NULL;
358 /* Chain up to parent's dispose () method. */
359 G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->dispose (object);
363 camel_vee_data_cache_finalize (GObject *object)
365 CamelVeeDataCache *data_cache;
367 data_cache = CAMEL_VEE_DATA_CACHE (object);
368 if (data_cache->priv) {
369 g_mutex_clear (&data_cache->priv->sf_mutex);
370 g_mutex_clear (&data_cache->priv->mi_mutex);
373 /* Chain up to parent's finalize () method. */
374 G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->finalize (object);
378 camel_vee_data_cache_class_init (CamelVeeDataCacheClass *class)
380 GObjectClass *object_class;
382 g_type_class_add_private (class, sizeof (CamelVeeDataCachePrivate));
384 object_class = G_OBJECT_CLASS (class);
385 object_class->dispose = camel_vee_data_cache_dispose;
386 object_class->finalize = camel_vee_data_cache_finalize;
390 camel_vee_data_cache_init (CamelVeeDataCache *data_cache)
392 data_cache->priv = G_TYPE_INSTANCE_GET_PRIVATE (data_cache, CAMEL_TYPE_VEE_DATA_CACHE, CamelVeeDataCachePrivate);
394 g_mutex_init (&data_cache->priv->sf_mutex);
395 data_cache->priv->subfolder_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
397 g_mutex_init (&data_cache->priv->mi_mutex);
398 data_cache->priv->orig_message_uid_hash = g_hash_table_new_full (vee_data_hash, vee_data_equal, g_free, g_object_unref);
399 data_cache->priv->vee_message_uid_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
403 * camel_vee_data_cache_new:
410 camel_vee_data_cache_new (void)
412 return g_object_new (CAMEL_TYPE_VEE_DATA_CACHE, NULL);
416 * camel_vee_data_cache_add_subfolder:
423 camel_vee_data_cache_add_subfolder (CamelVeeDataCache *data_cache,
424 CamelFolder *subfolder)
426 CamelVeeSubfolderData *sf_data;
428 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
429 g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
431 g_mutex_lock (&data_cache->priv->mi_mutex);
432 g_mutex_lock (&data_cache->priv->sf_mutex);
434 sf_data = g_hash_table_lookup (data_cache->priv->subfolder_hash, subfolder);
439 sf_data = camel_vee_subfolder_data_new (subfolder);
440 g_hash_table_insert (data_cache->priv->subfolder_hash, subfolder, sf_data);
442 /* camel_vee_data_cache_get_message_info_data() caches uids on demand,
443 * while here are cached all known uids in once - it is better when
444 * the folder is used in Unmatched folder, where the uid/vuid will
445 * be used in the vfolder or Unmatched folder anyway */
446 uids = camel_folder_get_uids (subfolder);
448 for (ii = 0; ii < uids->len; ii++) {
450 CamelVeeMessageInfoData *mi_data;
452 /* make sure the orig_message_uid comes from the string pool */
453 vdata.folder = subfolder;
454 vdata.orig_message_uid = camel_pstring_strdup (uids->pdata[ii]);
456 mi_data = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata);
460 mi_data = camel_vee_message_info_data_new (sf_data, vdata.orig_message_uid);
462 hash_data = g_new0 (VeeData, 1);
463 hash_data->folder = subfolder;
464 hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data);
466 g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, mi_data);
467 g_hash_table_insert (
468 data_cache->priv->vee_message_uid_hash,
469 (gpointer) camel_vee_message_info_data_get_vee_message_uid (mi_data),
473 camel_pstring_free (vdata.orig_message_uid);
476 camel_folder_free_uids (subfolder, uids);
480 g_mutex_unlock (&data_cache->priv->sf_mutex);
481 g_mutex_unlock (&data_cache->priv->mi_mutex);
485 remove_vee_by_folder_cb (gpointer key,
489 CamelVeeMessageInfoData *mi_data = value;
490 CamelVeeSubfolderData *sf_data;
491 CamelFolder *folder = user_data;
496 sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
497 return sf_data && camel_vee_subfolder_data_get_folder (sf_data) == folder;
501 remove_orig_by_folder_cb (gpointer key,
505 VeeData *vee_data = key;
506 CamelFolder *folder = user_data;
508 return vee_data && vee_data->folder == folder;
512 * camel_vee_data_cache_remove_subfolder:
519 camel_vee_data_cache_remove_subfolder (CamelVeeDataCache *data_cache,
520 CamelFolder *subfolder)
522 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
523 g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
525 g_mutex_lock (&data_cache->priv->mi_mutex);
526 g_mutex_lock (&data_cache->priv->sf_mutex);
528 g_hash_table_foreach_remove (data_cache->priv->vee_message_uid_hash, remove_vee_by_folder_cb, subfolder);
529 g_hash_table_foreach_remove (data_cache->priv->orig_message_uid_hash, remove_orig_by_folder_cb, subfolder);
530 g_hash_table_remove (data_cache->priv->subfolder_hash, subfolder);
532 g_mutex_unlock (&data_cache->priv->sf_mutex);
533 g_mutex_unlock (&data_cache->priv->mi_mutex);
537 * camel_vee_data_cache_get_subfolder_data:
543 CamelVeeSubfolderData *
544 camel_vee_data_cache_get_subfolder_data (CamelVeeDataCache *data_cache,
547 CamelVeeSubfolderData *res;
549 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL);
550 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
552 g_mutex_lock (&data_cache->priv->sf_mutex);
554 res = g_hash_table_lookup (data_cache->priv->subfolder_hash, folder);
556 res = camel_vee_subfolder_data_new (folder);
557 g_hash_table_insert (data_cache->priv->subfolder_hash, folder, res);
562 g_mutex_unlock (&data_cache->priv->sf_mutex);
568 * camel_vee_data_cache_contains_message_info_data:
570 * Returns whether data_cache contains certain UID for certain folder;
571 * instead of camel_vee_data_cache_get_message_info_data() only
572 * returns FALSE if not, while camel_vee_data_cache_get_message_info_data()
573 * auto-adds it to data_cache.
578 camel_vee_data_cache_contains_message_info_data (CamelVeeDataCache *data_cache,
580 const gchar *orig_message_uid)
585 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), FALSE);
586 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
587 g_return_val_if_fail (orig_message_uid != NULL, FALSE);
589 g_mutex_lock (&data_cache->priv->mi_mutex);
591 /* make sure the orig_message_uid comes from the string pool */
592 vdata.folder = folder;
593 vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid);
595 res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata) != NULL;
597 camel_pstring_free (vdata.orig_message_uid);
599 g_mutex_unlock (&data_cache->priv->mi_mutex);
605 * camel_vee_data_cache_get_message_info_data:
611 CamelVeeMessageInfoData *
612 camel_vee_data_cache_get_message_info_data (CamelVeeDataCache *data_cache,
614 const gchar *orig_message_uid)
616 CamelVeeMessageInfoData *res;
619 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL);
620 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
621 g_return_val_if_fail (orig_message_uid != NULL, NULL);
623 g_mutex_lock (&data_cache->priv->mi_mutex);
625 /* make sure the orig_message_uid comes from the string pool */
626 vdata.folder = folder;
627 vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid);
629 res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata);
632 CamelVeeSubfolderData *sf_data;
634 /* this locks also priv->sf_mutex */
635 sf_data = camel_vee_data_cache_get_subfolder_data (data_cache, folder);
637 camel_pstring_free (vdata.orig_message_uid);
638 g_mutex_unlock (&data_cache->priv->mi_mutex);
639 g_return_val_if_fail (sf_data != NULL, NULL);
642 res = camel_vee_message_info_data_new (sf_data, orig_message_uid);
644 /* res holds the reference now */
645 g_object_unref (sf_data);
647 hash_data = g_new0 (VeeData, 1);
648 hash_data->folder = folder;
649 hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (res);
651 g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, res);
652 g_hash_table_insert (
653 data_cache->priv->vee_message_uid_hash,
654 (gpointer) camel_vee_message_info_data_get_vee_message_uid (res),
658 camel_pstring_free (vdata.orig_message_uid);
661 g_mutex_unlock (&data_cache->priv->mi_mutex);
667 * camel_vee_data_cache_get_message_info_data_by_vuid:
673 CamelVeeMessageInfoData *
674 camel_vee_data_cache_get_message_info_data_by_vuid (CamelVeeDataCache *data_cache,
675 const gchar *vee_message_uid)
677 CamelVeeMessageInfoData *res;
680 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL);
681 g_return_val_if_fail (vee_message_uid != NULL, NULL);
683 g_mutex_lock (&data_cache->priv->mi_mutex);
685 /* make sure vee_message_uid comes from the string pool */
686 vuid = camel_pstring_strdup (vee_message_uid);
688 res = g_hash_table_lookup (data_cache->priv->vee_message_uid_hash, vuid);
692 g_mutex_unlock (&data_cache->priv->mi_mutex);
694 camel_pstring_free (vuid);
699 struct ForeachMiData {
700 CamelFolder *fromfolder;
701 void (* func) (CamelVeeMessageInfoData *mi_data,
702 CamelFolder *subfolder,
708 cvdc_foreach_mi_data_cb (gpointer key,
712 VeeData *vdata = key;
713 CamelVeeMessageInfoData *mi_data = value;
714 struct ForeachMiData *fmd = user_data;
716 g_return_if_fail (key != NULL);
717 g_return_if_fail (value != NULL);
718 g_return_if_fail (user_data != NULL);
720 if (!fmd->fromfolder || fmd->fromfolder == vdata->folder)
721 fmd->func (mi_data, vdata->folder, fmd->user_data);
725 * camel_vee_data_cache_foreach_message_info_data:
732 camel_vee_data_cache_foreach_message_info_data (CamelVeeDataCache *data_cache,
733 CamelFolder *fromfolder,
734 void (* func) (CamelVeeMessageInfoData *mi_data,
735 CamelFolder *subfolder,
739 struct ForeachMiData fmd;
741 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
742 g_return_if_fail (func != NULL);
744 g_mutex_lock (&data_cache->priv->mi_mutex);
746 fmd.fromfolder = fromfolder;
748 fmd.user_data = user_data;
750 g_hash_table_foreach (data_cache->priv->orig_message_uid_hash, cvdc_foreach_mi_data_cb, &fmd);
752 g_mutex_unlock (&data_cache->priv->mi_mutex);
756 * camel_vee_data_cache_remove_message_info_data:
763 camel_vee_data_cache_remove_message_info_data (CamelVeeDataCache *data_cache,
764 CamelVeeMessageInfoData *mi_data)
767 CamelVeeSubfolderData *sf_data;
770 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
771 g_return_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (mi_data));
773 g_mutex_lock (&data_cache->priv->mi_mutex);
775 g_object_ref (mi_data);
777 sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
779 vdata.folder = camel_vee_subfolder_data_get_folder (sf_data);
780 vdata.orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data);
781 vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data);
783 g_hash_table_remove (data_cache->priv->vee_message_uid_hash, vuid);
784 g_hash_table_remove (data_cache->priv->orig_message_uid_hash, &vdata);
786 g_object_unref (mi_data);
788 g_mutex_unlock (&data_cache->priv->mi_mutex);