1 /*
2 * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Milan Crha <mcrha@redhat.com>
17 */
18
19 #include "evolution-data-server-config.h"
20
21 #include "camel-string-utils.h"
22 #include "camel-store.h"
23
24 #include "camel-vee-data-cache.h"
25
26 struct _CamelVeeSubfolderDataPrivate {
27 CamelFolder *folder;
28 const gchar *folder_id; /* stored in string pool */
29 };
30
G_DEFINE_TYPE_WITH_PRIVATE(CamelVeeSubfolderData,camel_vee_subfolder_data,G_TYPE_OBJECT)31 G_DEFINE_TYPE_WITH_PRIVATE (
32 CamelVeeSubfolderData,
33 camel_vee_subfolder_data,
34 G_TYPE_OBJECT)
35
36 static void
37 vee_subfolder_data_dispose (GObject *object)
38 {
39 CamelVeeSubfolderDataPrivate *priv;
40
41 priv = CAMEL_VEE_SUBFOLDER_DATA (object)->priv;
42
43 g_clear_object (&priv->folder);
44
45 /* Chain up to parent's dispose() method. */
46 G_OBJECT_CLASS (camel_vee_subfolder_data_parent_class)->
47 dispose (object);
48 }
49
50 static void
vee_subfolder_data_finalize(GObject * object)51 vee_subfolder_data_finalize (GObject *object)
52 {
53 CamelVeeSubfolderDataPrivate *priv;
54
55 priv = CAMEL_VEE_SUBFOLDER_DATA (object)->priv;
56
57 if (priv->folder_id != NULL)
58 camel_pstring_free (priv->folder_id);
59
60 /* Chain up to parent's finalize() method. */
61 G_OBJECT_CLASS (camel_vee_subfolder_data_parent_class)->
62 finalize (object);
63 }
64
65 static void
camel_vee_subfolder_data_class_init(CamelVeeSubfolderDataClass * class)66 camel_vee_subfolder_data_class_init (CamelVeeSubfolderDataClass *class)
67 {
68 GObjectClass *object_class;
69
70 object_class = G_OBJECT_CLASS (class);
71 object_class->dispose = vee_subfolder_data_dispose;
72 object_class->finalize = vee_subfolder_data_finalize;
73 }
74
75 static void
camel_vee_subfolder_data_init(CamelVeeSubfolderData * data)76 camel_vee_subfolder_data_init (CamelVeeSubfolderData *data)
77 {
78 data->priv = camel_vee_subfolder_data_get_instance_private (data);
79 }
80
81 static void
vee_subfolder_data_hash_folder(CamelFolder * folder,gchar buffer[8])82 vee_subfolder_data_hash_folder (CamelFolder *folder,
83 gchar buffer[8])
84 {
85 CamelStore *parent_store;
86 GChecksum *checksum;
87 guint8 *digest;
88 gsize length;
89 gint state = 0, save = 0;
90 gchar *ptr_string;
91 const gchar *uid;
92 gint i;
93
94 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
95 digest = g_alloca (length);
96
97 checksum = g_checksum_new (G_CHECKSUM_MD5);
98 parent_store = camel_folder_get_parent_store (folder);
99 uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
100 g_checksum_update (checksum, (guchar *) uid, -1);
101
102 ptr_string = g_strdup_printf ("%p", folder);
103 g_checksum_update (checksum, (guchar *) ptr_string, -1);
104 g_free (ptr_string);
105
106 g_checksum_get_digest (checksum, digest, &length);
107 g_checksum_free (checksum);
108
109 g_base64_encode_step (digest, 6, FALSE, buffer, &state, &save);
110 g_base64_encode_close (FALSE, buffer, &state, &save);
111
112 for (i = 0; i < 8; i++) {
113 if (buffer[i] == '+')
114 buffer[i] = '.';
115 if (buffer[i] == '/')
116 buffer[i] = '_';
117 }
118 }
119
120 /**
121 * camel_vee_subfolder_data_new:
122 * @folder: a #CamelFolder for which create the object
123 *
124 * Creates a new #CamelVeeSubfolderData object for the given @folder.
125 * The @folder is referenced for later use.
126 *
127 * Returns: (transfer full): a new #CamelVeeSubfolderData. Use g_object_unref()
128 * to unref it, when no longer needed.
129 *
130 * Since: 3.6
131 **/
132 CamelVeeSubfolderData *
camel_vee_subfolder_data_new(CamelFolder * folder)133 camel_vee_subfolder_data_new (CamelFolder *folder)
134 {
135 CamelVeeSubfolderData *data;
136 gchar buffer[8], *folder_id;
137
138 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
139
140 data = g_object_new (CAMEL_TYPE_VEE_SUBFOLDER_DATA, NULL);
141 data->priv->folder = g_object_ref (folder);
142
143 vee_subfolder_data_hash_folder (folder, buffer);
144 folder_id = g_strndup (buffer, 8);
145
146 data->priv->folder_id = camel_pstring_add (folder_id, TRUE);
147
148 return data;
149 }
150
151 /**
152 * camel_vee_subfolder_data_get_folder:
153 * @data: a CamelVeeSubfolderData
154 *
155 * Returns: (transfer none): a #CamelFolder to which this @data was created
156 *
157 * Since: 3.6
158 **/
159 CamelFolder *
camel_vee_subfolder_data_get_folder(CamelVeeSubfolderData * data)160 camel_vee_subfolder_data_get_folder (CamelVeeSubfolderData *data)
161 {
162 g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL);
163
164 return data->priv->folder;
165 }
166
167 /**
168 * camel_vee_subfolder_data_get_folder_id:
169 * @data: a CamelVeeSubfolderData
170 *
171 * Returns: (transfer none): a folder ID for this subfolder @data
172 *
173 * Since: 3.6
174 **/
175 const gchar *
camel_vee_subfolder_data_get_folder_id(CamelVeeSubfolderData * data)176 camel_vee_subfolder_data_get_folder_id (CamelVeeSubfolderData *data)
177 {
178 g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (data), NULL);
179
180 return data->priv->folder_id;
181 }
182
183 /* ----------------------------------------------------------------------- */
184
185 struct _CamelVeeMessageInfoDataPrivate {
186 CamelVeeSubfolderData *subfolder_data;
187 const gchar *orig_message_uid; /* stored in string pool */
188 const gchar *vee_message_uid; /* stored in string pool */
189 };
190
G_DEFINE_TYPE_WITH_PRIVATE(CamelVeeMessageInfoData,camel_vee_message_info_data,G_TYPE_OBJECT)191 G_DEFINE_TYPE_WITH_PRIVATE (
192 CamelVeeMessageInfoData,
193 camel_vee_message_info_data,
194 G_TYPE_OBJECT)
195
196 static void
197 vee_message_info_data_dispose (GObject *object)
198 {
199 CamelVeeMessageInfoDataPrivate *priv;
200
201 priv = CAMEL_VEE_MESSAGE_INFO_DATA (object)->priv;
202
203 g_clear_object (&priv->subfolder_data);
204
205 /* Chain up to parent's dispose() method. */
206 G_OBJECT_CLASS (camel_vee_message_info_data_parent_class)->
207 dispose (object);
208 }
209
210 static void
vee_message_info_data_finalize(GObject * object)211 vee_message_info_data_finalize (GObject *object)
212 {
213 CamelVeeMessageInfoDataPrivate *priv;
214
215 priv = CAMEL_VEE_MESSAGE_INFO_DATA (object)->priv;
216
217 if (priv->orig_message_uid != NULL)
218 camel_pstring_free (priv->orig_message_uid);
219
220 if (priv->vee_message_uid != NULL)
221 camel_pstring_free (priv->vee_message_uid);
222
223 /* Chain up to parent's finalize() method. */
224 G_OBJECT_CLASS (camel_vee_message_info_data_parent_class)->
225 finalize (object);
226 }
227
228 static void
camel_vee_message_info_data_class_init(CamelVeeMessageInfoDataClass * class)229 camel_vee_message_info_data_class_init (CamelVeeMessageInfoDataClass *class)
230 {
231 GObjectClass *object_class;
232
233 object_class = G_OBJECT_CLASS (class);
234 object_class->dispose = vee_message_info_data_dispose;
235 object_class->finalize = vee_message_info_data_finalize;
236 }
237
238 static void
camel_vee_message_info_data_init(CamelVeeMessageInfoData * data)239 camel_vee_message_info_data_init (CamelVeeMessageInfoData *data)
240 {
241 data->priv = camel_vee_message_info_data_get_instance_private (data);
242 }
243
244 /**
245 * camel_vee_message_info_data_new:
246 * @subfolder_data: a #CamelVeeSubfolderData
247 * @orig_message_uid: original message info's UID
248 *
249 * Returns: (transfer full): a new #CamelVeeMessageInfoData which references
250 * message info with UID @orig_message_uid froma folder managed by @subfolder_data.
251 * Unref the returned object with g_object_unref(), when no longer needed.
252 *
253 * Since: 3.6
254 **/
255 CamelVeeMessageInfoData *
camel_vee_message_info_data_new(CamelVeeSubfolderData * subfolder_data,const gchar * orig_message_uid)256 camel_vee_message_info_data_new (CamelVeeSubfolderData *subfolder_data,
257 const gchar *orig_message_uid)
258 {
259 CamelVeeMessageInfoData *data;
260 gchar *vee_message_uid;
261
262 g_return_val_if_fail (CAMEL_IS_VEE_SUBFOLDER_DATA (subfolder_data), NULL);
263 g_return_val_if_fail (orig_message_uid != NULL, NULL);
264
265 data = g_object_new (CAMEL_TYPE_VEE_MESSAGE_INFO_DATA, NULL);
266 data->priv->subfolder_data = g_object_ref (subfolder_data);
267
268 vee_message_uid = g_strconcat (camel_vee_subfolder_data_get_folder_id (subfolder_data), orig_message_uid, NULL);
269
270 data->priv->orig_message_uid = camel_pstring_strdup (orig_message_uid);
271 data->priv->vee_message_uid = camel_pstring_add (vee_message_uid, TRUE);
272
273 return data;
274 }
275
276 /**
277 * camel_vee_message_info_data_get_subfolder_data:
278 * @data: a CamelVeeMessageInfoData
279 *
280 * Returns: (transfer none): A #CamelVeeSubfolderData for which
281 * the @data had been created.
282 *
283 * Since: 3.6
284 **/
285 CamelVeeSubfolderData *
camel_vee_message_info_data_get_subfolder_data(CamelVeeMessageInfoData * data)286 camel_vee_message_info_data_get_subfolder_data (CamelVeeMessageInfoData *data)
287 {
288 g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL);
289
290 return data->priv->subfolder_data;
291 }
292
293 /**
294 * camel_vee_message_info_data_get_orig_message_uid:
295 * @data: a CamelVeeMessageInfoData
296 *
297 * Returns: (transfer none): The original message info's UID, for which
298 * the @data had been created.
299 *
300 * Since: 3.6
301 **/
302 const gchar *
camel_vee_message_info_data_get_orig_message_uid(CamelVeeMessageInfoData * data)303 camel_vee_message_info_data_get_orig_message_uid (CamelVeeMessageInfoData *data)
304 {
305 g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL);
306
307 return data->priv->orig_message_uid;
308 }
309
310 /**
311 * camel_vee_message_info_data_get_vee_message_uid:
312 * @data: a CamelVeeMessageInfoData
313 *
314 * Returns: (transfer none): Message UID corresponding to this virtual
315 * message info @data.
316 *
317 * Since: 3.6
318 **/
319 const gchar *
camel_vee_message_info_data_get_vee_message_uid(CamelVeeMessageInfoData * data)320 camel_vee_message_info_data_get_vee_message_uid (CamelVeeMessageInfoData *data)
321 {
322 g_return_val_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (data), NULL);
323
324 return data->priv->vee_message_uid;
325 }
326
327 /* ----------------------------------------------------------------------- */
328
329 struct _CamelVeeDataCachePrivate {
330 GMutex sf_mutex; /* guards subfolder_hash */
331 GHashTable *subfolder_hash; /* CamelFolder * => CamelVeeSubfolderData * */
332
333 GMutex mi_mutex; /* guards message_info_hash */
334 GHashTable *orig_message_uid_hash; /* VeeData * => CamelVeeMessageInfoData * */
335 GHashTable *vee_message_uid_hash; /* const gchar *vee_uid => CamelVeeMessageInfoData * */
336 };
337
338 G_DEFINE_TYPE_WITH_PRIVATE (CamelVeeDataCache, camel_vee_data_cache, G_TYPE_OBJECT)
339
340 typedef struct _VeeData {
341 CamelFolder *folder;
342 const gchar *orig_message_uid;
343 } VeeData;
344
345 static VeeData *
vee_data_new(void)346 vee_data_new (void)
347 {
348 return g_slice_new0 (VeeData);
349 }
350
351 static void
vee_data_free(gpointer ptr)352 vee_data_free (gpointer ptr)
353 {
354 if (ptr)
355 g_slice_free (VeeData, ptr);
356 }
357
358 static guint
vee_data_hash(gconstpointer ptr)359 vee_data_hash (gconstpointer ptr)
360 {
361 const VeeData *vee_data = ptr;
362
363 if (!vee_data)
364 return 0;
365
366 return g_direct_hash (vee_data->folder)
367 + g_str_hash (vee_data->orig_message_uid);
368 }
369
370 static gboolean
vee_data_equal(gconstpointer v1,gconstpointer v2)371 vee_data_equal (gconstpointer v1,
372 gconstpointer v2)
373 {
374 const VeeData *vee_data1 = v1, *vee_data2 = v2;
375
376 if (!v1 || !v2)
377 return v1 == v2;
378
379 /* can contain ponters directly, strings are always from the string pool */
380 return v1 == v2 ||
381 (vee_data1->folder == vee_data2->folder &&
382 vee_data1->orig_message_uid == vee_data2->orig_message_uid);
383 }
384
385 static void
vee_data_cache_dispose(GObject * object)386 vee_data_cache_dispose (GObject *object)
387 {
388 CamelVeeDataCachePrivate *priv;
389
390 priv = CAMEL_VEE_DATA_CACHE (object)->priv;
391 g_clear_pointer (&priv->subfolder_hash, g_hash_table_destroy);
392 g_clear_pointer (&priv->orig_message_uid_hash, g_hash_table_destroy);
393 g_clear_pointer (&priv->vee_message_uid_hash, g_hash_table_destroy);
394
395 /* Chain up to parent's dispose () method. */
396 G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->dispose (object);
397 }
398
399 static void
vee_data_cache_finalize(GObject * object)400 vee_data_cache_finalize (GObject *object)
401 {
402 CamelVeeDataCachePrivate *priv;
403
404 priv = CAMEL_VEE_DATA_CACHE (object)->priv;
405
406 g_mutex_clear (&priv->sf_mutex);
407 g_mutex_clear (&priv->mi_mutex);
408
409 /* Chain up to parent's finalize () method. */
410 G_OBJECT_CLASS (camel_vee_data_cache_parent_class)->finalize (object);
411 }
412
413 static void
camel_vee_data_cache_class_init(CamelVeeDataCacheClass * class)414 camel_vee_data_cache_class_init (CamelVeeDataCacheClass *class)
415 {
416 GObjectClass *object_class;
417
418 object_class = G_OBJECT_CLASS (class);
419 object_class->dispose = vee_data_cache_dispose;
420 object_class->finalize = vee_data_cache_finalize;
421 }
422
423 static void
camel_vee_data_cache_init(CamelVeeDataCache * data_cache)424 camel_vee_data_cache_init (CamelVeeDataCache *data_cache)
425 {
426 data_cache->priv = camel_vee_data_cache_get_instance_private (data_cache);
427
428 g_mutex_init (&data_cache->priv->sf_mutex);
429 data_cache->priv->subfolder_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
430
431 g_mutex_init (&data_cache->priv->mi_mutex);
432 data_cache->priv->orig_message_uid_hash = g_hash_table_new_full (vee_data_hash, vee_data_equal, vee_data_free, g_object_unref);
433 data_cache->priv->vee_message_uid_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
434 }
435
436 /**
437 * camel_vee_data_cache_new:
438 *
439 * Returns: (transfer full): a new #CamelVeeDataCache; unref it
440 * with g_object_unref(), when no longer needed.
441 *
442 * Since: 3.6
443 **/
444 CamelVeeDataCache *
camel_vee_data_cache_new(void)445 camel_vee_data_cache_new (void)
446 {
447 return g_object_new (CAMEL_TYPE_VEE_DATA_CACHE, NULL);
448 }
449
450 /**
451 * camel_vee_data_cache_add_subfolder:
452 * @data_cache: a #CamelVeeDataCache
453 * @subfolder: a #CamelFolder
454 *
455 * Adds the @subfolder to the @data_cache to be tracked by it. The @subfolder
456 * is referenced for later use. The function does nothing when the @subfolder
457 * is already in the @data_cache. The subfolders can be removed with
458 * camel_vee_data_cache_remove_subfolder().
459 *
460 * Since: 3.6
461 **/
462 void
camel_vee_data_cache_add_subfolder(CamelVeeDataCache * data_cache,CamelFolder * subfolder)463 camel_vee_data_cache_add_subfolder (CamelVeeDataCache *data_cache,
464 CamelFolder *subfolder)
465 {
466 CamelVeeSubfolderData *sf_data;
467
468 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
469 g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
470
471 g_mutex_lock (&data_cache->priv->mi_mutex);
472 g_mutex_lock (&data_cache->priv->sf_mutex);
473
474 sf_data = g_hash_table_lookup (data_cache->priv->subfolder_hash, subfolder);
475 if (!sf_data) {
476 GPtrArray *uids;
477 gint ii;
478
479 sf_data = camel_vee_subfolder_data_new (subfolder);
480 g_hash_table_insert (data_cache->priv->subfolder_hash, subfolder, sf_data);
481
482 /* camel_vee_data_cache_get_message_info_data() caches uids on demand,
483 * while here are cached all known uids in once - it is better when
484 * the folder is used in Unmatched folder, where the uid/vuid will
485 * be used in the vfolder or Unmatched folder anyway */
486 uids = camel_folder_get_uids (subfolder);
487 if (uids) {
488 for (ii = 0; ii < uids->len; ii++) {
489 VeeData vdata;
490 CamelVeeMessageInfoData *mi_data;
491
492 /* make sure the orig_message_uid comes from the string pool */
493 vdata.folder = subfolder;
494 vdata.orig_message_uid = camel_pstring_strdup (uids->pdata[ii]);
495
496 mi_data = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata);
497 if (!mi_data) {
498 VeeData *hash_data;
499
500 mi_data = camel_vee_message_info_data_new (sf_data, vdata.orig_message_uid);
501
502 hash_data = vee_data_new ();
503 hash_data->folder = subfolder;
504 hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data);
505
506 g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, mi_data);
507 g_hash_table_insert (
508 data_cache->priv->vee_message_uid_hash,
509 (gpointer) camel_vee_message_info_data_get_vee_message_uid (mi_data),
510 mi_data);
511 }
512
513 camel_pstring_free (vdata.orig_message_uid);
514 }
515
516 camel_folder_free_uids (subfolder, uids);
517 }
518 }
519
520 g_mutex_unlock (&data_cache->priv->sf_mutex);
521 g_mutex_unlock (&data_cache->priv->mi_mutex);
522 }
523
524 static gboolean
remove_vee_by_folder_cb(gpointer key,gpointer value,gpointer user_data)525 remove_vee_by_folder_cb (gpointer key,
526 gpointer value,
527 gpointer user_data)
528 {
529 CamelVeeMessageInfoData *mi_data = value;
530 CamelVeeSubfolderData *sf_data;
531 CamelFolder *folder = user_data;
532
533 if (!mi_data)
534 return FALSE;
535
536 sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
537 return sf_data && camel_vee_subfolder_data_get_folder (sf_data) == folder;
538 }
539
540 static gboolean
remove_orig_by_folder_cb(gpointer key,gpointer value,gpointer user_data)541 remove_orig_by_folder_cb (gpointer key,
542 gpointer value,
543 gpointer user_data)
544 {
545 VeeData *vee_data = key;
546 CamelFolder *folder = user_data;
547
548 return vee_data && vee_data->folder == folder;
549 }
550
551 /**
552 * camel_vee_data_cache_remove_subfolder:
553 * @data_cache: a #CamelVeeDataCache
554 * @subfolder: a #CamelFolder to remove
555 *
556 * Removes given @subfolder from the @data_cache, which had been
557 * previously added with camel_vee_data_cache_add_subfolder().
558 * The function does nothing, when the @subfolder is not part
559 * of the @data_cache.
560 *
561 * Since: 3.6
562 **/
563 void
camel_vee_data_cache_remove_subfolder(CamelVeeDataCache * data_cache,CamelFolder * subfolder)564 camel_vee_data_cache_remove_subfolder (CamelVeeDataCache *data_cache,
565 CamelFolder *subfolder)
566 {
567 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
568 g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
569
570 g_mutex_lock (&data_cache->priv->mi_mutex);
571 g_mutex_lock (&data_cache->priv->sf_mutex);
572
573 g_hash_table_foreach_remove (data_cache->priv->vee_message_uid_hash, remove_vee_by_folder_cb, subfolder);
574 g_hash_table_foreach_remove (data_cache->priv->orig_message_uid_hash, remove_orig_by_folder_cb, subfolder);
575 g_hash_table_remove (data_cache->priv->subfolder_hash, subfolder);
576
577 g_mutex_unlock (&data_cache->priv->sf_mutex);
578 g_mutex_unlock (&data_cache->priv->mi_mutex);
579 }
580
581 /**
582 * camel_vee_data_cache_get_subfolder_data:
583 * @data_cache: a #CamelVeeDataCache
584 * @folder: a #CamelFolder for which to return subfolder data
585 *
586 * Returns a #CamelVeeSubfolderData for the given @folder.
587 *
588 * Returns: (transfer full): a referenced #CamelVeeSubfolderData; unref it
589 * with g_object_unref(), when no longer needed.
590 *
591 * Since: 3.6
592 **/
593 CamelVeeSubfolderData *
camel_vee_data_cache_get_subfolder_data(CamelVeeDataCache * data_cache,CamelFolder * folder)594 camel_vee_data_cache_get_subfolder_data (CamelVeeDataCache *data_cache,
595 CamelFolder *folder)
596 {
597 CamelVeeSubfolderData *res;
598
599 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL);
600 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
601
602 g_mutex_lock (&data_cache->priv->sf_mutex);
603
604 res = g_hash_table_lookup (data_cache->priv->subfolder_hash, folder);
605 if (!res) {
606 res = camel_vee_subfolder_data_new (folder);
607 g_hash_table_insert (data_cache->priv->subfolder_hash, folder, res);
608 }
609
610 g_object_ref (res);
611
612 g_mutex_unlock (&data_cache->priv->sf_mutex);
613
614 return res;
615 }
616
617 /**
618 * camel_vee_data_cache_contains_message_info_data:
619 * @data_cache: a #CamelVeeDataCache
620 * @folder: a #CamelFolder to which the @orig_message_uid belongs
621 * @orig_message_uid: a message UID from the @folder to check
622 *
623 * Returns whether data_cache contains given @orig_message_uid for the given @folder.
624 * Unlike camel_vee_data_cache_get_message_info_data(), this only
625 * returns %FALSE if not, while camel_vee_data_cache_get_message_info_data()
626 * auto-adds it to data_cache.
627 *
628 * Since: 3.6
629 */
630 gboolean
camel_vee_data_cache_contains_message_info_data(CamelVeeDataCache * data_cache,CamelFolder * folder,const gchar * orig_message_uid)631 camel_vee_data_cache_contains_message_info_data (CamelVeeDataCache *data_cache,
632 CamelFolder *folder,
633 const gchar *orig_message_uid)
634 {
635 gboolean res;
636 VeeData vdata;
637
638 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), FALSE);
639 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
640 g_return_val_if_fail (orig_message_uid != NULL, FALSE);
641
642 g_mutex_lock (&data_cache->priv->mi_mutex);
643
644 /* make sure the orig_message_uid comes from the string pool */
645 vdata.folder = folder;
646 vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid);
647
648 res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata) != NULL;
649
650 camel_pstring_free (vdata.orig_message_uid);
651
652 g_mutex_unlock (&data_cache->priv->mi_mutex);
653
654 return res;
655 }
656
657 /**
658 * camel_vee_data_cache_get_message_info_data:
659 * @data_cache: a #CamelVeeDataCache
660 * @folder: a #CamelFolder to which the @orig_message_uid belongs
661 * @orig_message_uid: a message UID from the @folder to return
662 *
663 * Returns a referenced #CamelVeeMessageInfoData referencing the given @folder
664 * and @orig_message_uid. If it's not part of the @data_cache, then it is
665 * created and auto-added. Use camel_vee_data_cache_contains_message_info_data()
666 * when you only want to check the existence, without adding it to the @data_cache.
667 *
668 * Returns: (transfer full): a referenced #CamelVeeMessageInfoData; unref it
669 * with g_object_unref(), when no longer needed.
670 *
671 * Since: 3.6
672 **/
673 CamelVeeMessageInfoData *
camel_vee_data_cache_get_message_info_data(CamelVeeDataCache * data_cache,CamelFolder * folder,const gchar * orig_message_uid)674 camel_vee_data_cache_get_message_info_data (CamelVeeDataCache *data_cache,
675 CamelFolder *folder,
676 const gchar *orig_message_uid)
677 {
678 CamelVeeMessageInfoData *res;
679 VeeData vdata;
680
681 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL);
682 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
683 g_return_val_if_fail (orig_message_uid != NULL, NULL);
684
685 g_mutex_lock (&data_cache->priv->mi_mutex);
686
687 /* make sure the orig_message_uid comes from the string pool */
688 vdata.folder = folder;
689 vdata.orig_message_uid = camel_pstring_strdup (orig_message_uid);
690
691 res = g_hash_table_lookup (data_cache->priv->orig_message_uid_hash, &vdata);
692 if (!res) {
693 VeeData *hash_data;
694 CamelVeeSubfolderData *sf_data;
695
696 /* this locks also priv->sf_mutex */
697 sf_data = camel_vee_data_cache_get_subfolder_data (data_cache, folder);
698 if (!sf_data) {
699 camel_pstring_free (vdata.orig_message_uid);
700 g_mutex_unlock (&data_cache->priv->mi_mutex);
701 g_return_val_if_fail (sf_data != NULL, NULL);
702 }
703
704 res = camel_vee_message_info_data_new (sf_data, orig_message_uid);
705
706 /* res holds the reference now */
707 g_object_unref (sf_data);
708
709 hash_data = vee_data_new ();
710 hash_data->folder = folder;
711 hash_data->orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (res);
712
713 g_hash_table_insert (data_cache->priv->orig_message_uid_hash, hash_data, res);
714 g_hash_table_insert (
715 data_cache->priv->vee_message_uid_hash,
716 (gpointer) camel_vee_message_info_data_get_vee_message_uid (res),
717 res);
718 }
719
720 camel_pstring_free (vdata.orig_message_uid);
721 g_object_ref (res);
722
723 g_mutex_unlock (&data_cache->priv->mi_mutex);
724
725 return res;
726 }
727
728 /**
729 * camel_vee_data_cache_get_message_info_data_by_vuid:
730 * @data_cache: a #CamelVeeDataCache
731 * @vee_message_uid: a message UID in the virtual folder
732 *
733 * Returns: (transfer full) (nullable): a referenced #CamelVeeMessageInfoData,
734 * which corresponds to the given @vee_message_uid, or %NULL, when no such
735 * message info with that virtual UID exists. Unref it with g_object_unref(),
736 * when no longer needed.
737 *
738 * Since: 3.6
739 **/
740 CamelVeeMessageInfoData *
camel_vee_data_cache_get_message_info_data_by_vuid(CamelVeeDataCache * data_cache,const gchar * vee_message_uid)741 camel_vee_data_cache_get_message_info_data_by_vuid (CamelVeeDataCache *data_cache,
742 const gchar *vee_message_uid)
743 {
744 CamelVeeMessageInfoData *res;
745 const gchar *vuid;
746
747 g_return_val_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache), NULL);
748 g_return_val_if_fail (vee_message_uid != NULL, NULL);
749
750 g_mutex_lock (&data_cache->priv->mi_mutex);
751
752 /* make sure vee_message_uid comes from the string pool */
753 vuid = camel_pstring_strdup (vee_message_uid);
754
755 res = g_hash_table_lookup (data_cache->priv->vee_message_uid_hash, vuid);
756 if (res)
757 g_object_ref (res);
758
759 g_mutex_unlock (&data_cache->priv->mi_mutex);
760
761 camel_pstring_free (vuid);
762
763 return res;
764 }
765
766 struct ForeachMiData {
767 CamelFolder *fromfolder;
768 CamelForeachInfoData func;
769 gpointer user_data;
770 };
771
772 static void
cvdc_foreach_mi_data_cb(gpointer key,gpointer value,gpointer user_data)773 cvdc_foreach_mi_data_cb (gpointer key,
774 gpointer value,
775 gpointer user_data)
776 {
777 VeeData *vdata = key;
778 CamelVeeMessageInfoData *mi_data = value;
779 struct ForeachMiData *fmd = user_data;
780
781 g_return_if_fail (key != NULL);
782 g_return_if_fail (value != NULL);
783 g_return_if_fail (user_data != NULL);
784
785 if (!fmd->fromfolder || fmd->fromfolder == vdata->folder)
786 fmd->func (mi_data, vdata->folder, fmd->user_data);
787 }
788
789 /**
790 * camel_vee_data_cache_foreach_message_info_data:
791 * @data_cache: a #CamelVeeDataCache
792 * @fromfolder: a #CamelFolder
793 * @func: (scope call) (closure user_data): a #CamelForeachInfoData function to call
794 * @user_data: user data to pass to the @func
795 *
796 * Calls the @func for each message info data from the given @fromfolder
797 *
798 * Since: 3.6
799 **/
800 void
camel_vee_data_cache_foreach_message_info_data(CamelVeeDataCache * data_cache,CamelFolder * fromfolder,CamelForeachInfoData func,gpointer user_data)801 camel_vee_data_cache_foreach_message_info_data (CamelVeeDataCache *data_cache,
802 CamelFolder *fromfolder,
803 CamelForeachInfoData func,
804 gpointer user_data)
805 {
806 struct ForeachMiData fmd;
807
808 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
809 g_return_if_fail (func != NULL);
810
811 g_mutex_lock (&data_cache->priv->mi_mutex);
812
813 fmd.fromfolder = fromfolder;
814 fmd.func = func;
815 fmd.user_data = user_data;
816
817 g_hash_table_foreach (data_cache->priv->orig_message_uid_hash, cvdc_foreach_mi_data_cb, &fmd);
818
819 g_mutex_unlock (&data_cache->priv->mi_mutex);
820 }
821
822 /**
823 * camel_vee_data_cache_remove_message_info_data:
824 * @data_cache: a #CamelVeeDataCache
825 * @mi_data: a #CamelVeeMessageInfoData to remove
826 *
827 * Removes given @mi_data from the @data_cache.
828 *
829 * Since: 3.6
830 **/
831 void
camel_vee_data_cache_remove_message_info_data(CamelVeeDataCache * data_cache,CamelVeeMessageInfoData * mi_data)832 camel_vee_data_cache_remove_message_info_data (CamelVeeDataCache *data_cache,
833 CamelVeeMessageInfoData *mi_data)
834 {
835 VeeData vdata;
836 CamelVeeSubfolderData *sf_data;
837 const gchar *vuid;
838
839 g_return_if_fail (CAMEL_IS_VEE_DATA_CACHE (data_cache));
840 g_return_if_fail (CAMEL_IS_VEE_MESSAGE_INFO_DATA (mi_data));
841
842 g_mutex_lock (&data_cache->priv->mi_mutex);
843
844 g_object_ref (mi_data);
845
846 sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
847
848 vdata.folder = camel_vee_subfolder_data_get_folder (sf_data);
849 vdata.orig_message_uid = camel_vee_message_info_data_get_orig_message_uid (mi_data);
850 vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data);
851
852 g_hash_table_remove (data_cache->priv->vee_message_uid_hash, vuid);
853 g_hash_table_remove (data_cache->priv->orig_message_uid_hash, &vdata);
854
855 g_object_unref (mi_data);
856
857 g_mutex_unlock (&data_cache->priv->mi_mutex);
858 }
859