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