1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * SECTION: e-cache
20  * @include: libebackend/libebackend.h
21  * @short_description: An SQLite data cache
22  *
23  * The #ECache is an abstract class which consists of the common
24  * parts which can be used by its descendants. It also allows
25  * storing offline state for the stored objects.
26  *
27  * The API is thread safe, with special considerations to be made
28  * around e_cache_lock() and e_cache_unlock() for
29  * the sake of isolating transactions across threads.
30  **/
31 
32 #include "evolution-data-server-config.h"
33 
34 #include <errno.h>
35 #include <sqlite3.h>
36 
37 #include <glib.h>
38 #include <glib/gi18n-lib.h>
39 #include <glib/gstdio.h>
40 
41 #include <camel/camel.h>
42 #include <libedataserver/libedataserver.h>
43 
44 #include "e-sqlite3-vfs.h"
45 
46 #include "e-cache.h"
47 
48 #define E_CACHE_KEY_VERSION	"version"
49 #define E_CACHE_KEY_REVISION	"revision"
50 
51 /* The number of SQLite virtual machine instructions that are
52  * evaluated at a time, the user passed GCancellable is
53  * checked between each batch of evaluated instructions.
54  */
55 #define E_CACHE_CANCEL_BATCH_SIZE	200
56 
57 /* How many rows to read when e_cache_foreach_update() */
58 #define E_CACHE_UPDATE_BATCH_SIZE	100
59 
60 struct _ECachePrivate {
61 	gchar *filename;
62 	sqlite3 *db;
63 
64 	GRecMutex lock;			/* Main API lock */
65 	guint32 in_transaction;		/* Nested transaction counter */
66 	ECacheLockType lock_type;	/* The lock type acquired for the current transaction */
67 	GCancellable *cancellable;	/* User passed GCancellable, we abort an operation if cancelled */
68 
69 	guint32 revision_change_frozen;
70 	gint revision_counter;
71 	gint64 last_revision_time;
72 	gboolean needs_revision_change;
73 };
74 
75 enum {
76 	BEFORE_PUT,
77 	BEFORE_REMOVE,
78 	REVISION_CHANGED,
79 	LAST_SIGNAL
80 };
81 
82 static guint signals[LAST_SIGNAL];
83 
84 G_DEFINE_QUARK (e-cache-error-quark, e_cache_error)
85 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(ECache,e_cache,G_TYPE_OBJECT)86 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ECache, e_cache, G_TYPE_OBJECT)
87 
88 G_DEFINE_BOXED_TYPE (ECacheColumnValues, e_cache_column_values, e_cache_column_values_copy, e_cache_column_values_free)
89 G_DEFINE_BOXED_TYPE (ECacheOfflineChange, e_cache_offline_change, e_cache_offline_change_copy, e_cache_offline_change_free)
90 G_DEFINE_BOXED_TYPE (ECacheColumnInfo, e_cache_column_info, e_cache_column_info_copy, e_cache_column_info_free)
91 
92 /**
93  * e_cache_column_values_new:
94  *
95  * Creates a new #ECacheColumnValues to store values for additional columns.
96  * The column names are compared case insensitively.
97  *
98  * Returns: (transfer full): a new #ECacheColumnValues. Free with e_cache_column_values_free(),
99  *    when no longer needed.
100  *
101  * Since: 3.26
102  **/
103 ECacheColumnValues *
104 e_cache_column_values_new (void)
105 {
106 	return (ECacheColumnValues *) g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, g_free);
107 }
108 
109 /**
110  * e_cache_column_values_copy:
111  * @other_columns: (nullable): an #ECacheColumnValues
112  *
113  * Returns: (transfer full): Copy of the @other_columns. Free with
114  *    e_cache_column_values_free(), when no longer needed.
115  *
116  * Since: 3.26
117  **/
118 ECacheColumnValues *
e_cache_column_values_copy(ECacheColumnValues * other_columns)119 e_cache_column_values_copy (ECacheColumnValues *other_columns)
120 {
121 	GHashTableIter iter;
122 	gpointer name, value;
123 	ECacheColumnValues *copy;
124 
125 	if (!other_columns)
126 		return NULL;
127 
128 	copy = e_cache_column_values_new ();
129 
130 	e_cache_column_values_init_iter (other_columns, &iter);
131 	while (g_hash_table_iter_next (&iter, &name, &value)) {
132 		e_cache_column_values_put (copy, name, value);
133 	}
134 
135 	return copy;
136 }
137 
138 /**
139  * e_cache_column_values_free:
140  * @other_columns: (nullable): an #ECacheColumnValues
141  *
142  * Frees previously allocated @other_columns with
143  * e_cache_column_values_new() or e_cache_column_values_copy().
144  *
145  * Since: 3.26
146  **/
147 void
e_cache_column_values_free(ECacheColumnValues * other_columns)148 e_cache_column_values_free (ECacheColumnValues *other_columns)
149 {
150 	if (other_columns)
151 		g_hash_table_destroy ((GHashTable *) other_columns);
152 }
153 
154 /**
155  * e_cache_column_values_put:
156  * @other_columns: an #ECacheColumnValues
157  * @name: a column name
158  * @value: (nullable): a column value
159  *
160  * Puts the @value for column @name. If contains a value for the same
161  * column, then it is replaced. This creates a copy of both @name
162  * and @value.
163  *
164  * Since: 3.26
165  **/
166 void
e_cache_column_values_put(ECacheColumnValues * other_columns,const gchar * name,const gchar * value)167 e_cache_column_values_put (ECacheColumnValues *other_columns,
168 			   const gchar *name,
169 			   const gchar *value)
170 {
171 	GHashTable *hash_table = (GHashTable *) other_columns;
172 
173 	g_return_if_fail (other_columns != NULL);
174 	g_return_if_fail (name != NULL);
175 
176 	g_hash_table_insert (hash_table, g_strdup (name), g_strdup (value));
177 }
178 
179 /**
180  * e_cache_column_values_take_value:
181  * @other_columns: an #ECacheColumnValues
182  * @name: a column name
183  * @value: (nullable) (in) (transfer full): a column value
184  *
185  * Puts the @value for column @name. If contains a value for the same
186  * column, then it is replaced. This creates a copy of the @name, but
187  * takes owner ship of the @value.
188  *
189  * Since: 3.26
190  **/
191 void
e_cache_column_values_take_value(ECacheColumnValues * other_columns,const gchar * name,gchar * value)192 e_cache_column_values_take_value (ECacheColumnValues *other_columns,
193 				  const gchar *name,
194 				  gchar *value)
195 {
196 	GHashTable *hash_table = (GHashTable *) other_columns;
197 
198 	g_return_if_fail (other_columns != NULL);
199 	g_return_if_fail (name != NULL);
200 
201 	g_hash_table_insert (hash_table, g_strdup (name), value);
202 }
203 
204 /**
205  * e_cache_column_values_take:
206  * @other_columns: an #ECacheColumnValues
207  * @name: (in) (transfer full): a column name
208  * @value: (nullable) (in) (transfer full): a column value
209  *
210  * Puts the @value for column @name. If contains a value for the same
211  * column, then it is replaced. This creates takes ownership of both
212  * the @name and the @value.
213  *
214  * Since: 3.26
215  **/
216 void
e_cache_column_values_take(ECacheColumnValues * other_columns,gchar * name,gchar * value)217 e_cache_column_values_take (ECacheColumnValues *other_columns,
218 			    gchar *name,
219 			    gchar *value)
220 {
221 	GHashTable *hash_table = (GHashTable *) other_columns;
222 
223 	g_return_if_fail (other_columns != NULL);
224 	g_return_if_fail (name != NULL);
225 
226 	g_hash_table_insert (hash_table, name, value);
227 }
228 
229 /**
230  * e_cache_column_values_contains:
231  * @other_columns: an #ECacheColumnValues
232  * @name: a column name
233  *
234  * Returns: Whether @other_columns contains column named @name.
235  *
236  * Since: 3.26
237  **/
238 gboolean
e_cache_column_values_contains(ECacheColumnValues * other_columns,const gchar * name)239 e_cache_column_values_contains (ECacheColumnValues *other_columns,
240 				const gchar *name)
241 {
242 	GHashTable *hash_table = (GHashTable *) other_columns;
243 
244 	g_return_val_if_fail (other_columns != NULL, FALSE);
245 	g_return_val_if_fail (name != NULL, FALSE);
246 
247 	return g_hash_table_contains (hash_table, name);
248 }
249 
250 /**
251  * e_cache_column_values_remove:
252  * @other_columns: an #ECacheColumnValues
253  * @name: a column name
254  *
255  * Removes value for the column named @name from @other_columns.
256  *
257  * Returns: Whether such column existed and had been removed.
258  *
259  * Since: 3.26
260  **/
261 gboolean
e_cache_column_values_remove(ECacheColumnValues * other_columns,const gchar * name)262 e_cache_column_values_remove (ECacheColumnValues *other_columns,
263 			      const gchar *name)
264 {
265 	GHashTable *hash_table = (GHashTable *) other_columns;
266 
267 	g_return_val_if_fail (other_columns != NULL, FALSE);
268 	g_return_val_if_fail (name != NULL, FALSE);
269 
270 	return g_hash_table_remove (hash_table, name);
271 }
272 
273 /**
274  * e_cache_column_values_remove_all:
275  * @other_columns: an #ECacheColumnValues
276  *
277  * Removes all values from the @other_columns, leaving it empty.
278  *
279  * Since: 3.26
280  **/
281 void
e_cache_column_values_remove_all(ECacheColumnValues * other_columns)282 e_cache_column_values_remove_all (ECacheColumnValues *other_columns)
283 {
284 	GHashTable *hash_table = (GHashTable *) other_columns;
285 
286 	g_return_if_fail (other_columns != NULL);
287 
288 	g_hash_table_remove_all (hash_table);
289 }
290 
291 /**
292  * e_cache_column_values_lookup:
293  * @other_columns: an #ECacheColumnValues
294  * @name: a column name
295  *
296  * Looks up currently stored value for the column named @name.
297  * As the values can be %NULL one cannot distinguish between
298  * a column which doesn't have stored any value and a column
299  * which has stored %NULL value. Use e_cache_column_values_contains()
300  * to check whether such column exitst in the @other_columns.
301  * The returned pointer is owned by @other_columns and is valid until
302  * the value is overwritten of the @other_columns freed.
303  *
304  * Returns: Stored value for the column named @name, or %NULL, if
305  *    no such column values is stored.
306  *
307  * Since: 3.26
308  **/
309 const gchar *
e_cache_column_values_lookup(ECacheColumnValues * other_columns,const gchar * name)310 e_cache_column_values_lookup (ECacheColumnValues *other_columns,
311 			      const gchar *name)
312 {
313 	GHashTable *hash_table = (GHashTable *) other_columns;
314 
315 	g_return_val_if_fail (other_columns != NULL, NULL);
316 	g_return_val_if_fail (name != NULL, NULL);
317 
318 	return g_hash_table_lookup (hash_table, name);
319 }
320 
321 /**
322  * e_cache_column_values_get_size:
323  * @other_columns: an #ECacheColumnValues
324  *
325  * Returns: How many columns are stored in the @other_columns.
326  *
327  * Since: 3.26
328  **/
329 guint
e_cache_column_values_get_size(ECacheColumnValues * other_columns)330 e_cache_column_values_get_size (ECacheColumnValues *other_columns)
331 {
332 	GHashTable *hash_table = (GHashTable *) other_columns;
333 
334 	g_return_val_if_fail (other_columns != NULL, 0);
335 
336 	return g_hash_table_size (hash_table);
337 }
338 
339 /**
340  * e_cache_column_values_init_iter:
341  * @other_columns: an #ECacheColumnValues
342  * @iter: a #GHashTableIter
343  *
344  * Initialized the @iter, thus the @other_columns can be traversed
345  * with g_hash_table_iter_next(). The key is a column name and
346  * the value is the corresponding column value.
347  *
348  * Since: 3.26
349  **/
350 void
e_cache_column_values_init_iter(ECacheColumnValues * other_columns,GHashTableIter * iter)351 e_cache_column_values_init_iter (ECacheColumnValues *other_columns,
352 				 GHashTableIter *iter)
353 {
354 	GHashTable *hash_table = (GHashTable *) other_columns;
355 
356 	g_return_if_fail (other_columns != NULL);
357 	g_return_if_fail (iter != NULL);
358 
359 	g_hash_table_iter_init (iter, hash_table);
360 }
361 
362 /**
363  * e_cache_offline_change_new:
364  * @uid: a unique object identifier
365  * @revision: (nullable): a revision of the object
366  * @object: (nullable): object itself
367  * @state: an #EOfflineState
368  *
369  * Creates a new #ECacheOfflineChange with the offline @state
370  * information for the given @uid.
371  *
372  * Returns: (transfer full): A new #ECacheOfflineChange. Free it with
373  *    e_cache_offline_change_free() when no longer needed.
374  *
375  * Since: 3.26
376  **/
377 ECacheOfflineChange *
e_cache_offline_change_new(const gchar * uid,const gchar * revision,const gchar * object,EOfflineState state)378 e_cache_offline_change_new (const gchar *uid,
379 			    const gchar *revision,
380 			    const gchar *object,
381 			    EOfflineState state)
382 {
383 	ECacheOfflineChange *change;
384 
385 	g_return_val_if_fail (uid != NULL, NULL);
386 
387 	change = g_slice_new0 (ECacheOfflineChange);
388 	change->uid = g_strdup (uid);
389 	change->revision = g_strdup (revision);
390 	change->object = g_strdup (object);
391 	change->state = state;
392 
393 	return change;
394 }
395 
396 /**
397  * e_cache_offline_change_copy:
398  * @change: (nullable): a source #ECacheOfflineChange to copy, or %NULL
399  *
400  * Returns: (transfer full): Copy of the given @change. Free it with
401  *    e_cache_offline_change_free() when no longer needed.
402  *    If the @change is %NULL, then returns %NULL as well.
403  *
404  * Since: 3.26
405  **/
406 ECacheOfflineChange *
e_cache_offline_change_copy(const ECacheOfflineChange * change)407 e_cache_offline_change_copy (const ECacheOfflineChange *change)
408 {
409 	if (!change)
410 		return NULL;
411 
412 	return e_cache_offline_change_new (change->uid, change->revision, change->object, change->state);
413 }
414 
415 /**
416  * e_cache_offline_change_free:
417  * @change: (nullable): an #ECacheOfflineChange
418  *
419  * Frees the @change structure, previously allocated with e_cache_offline_change_new()
420  * or e_cache_offline_change_copy().
421  *
422  * Since: 3.26
423  **/
424 void
e_cache_offline_change_free(gpointer change)425 e_cache_offline_change_free (gpointer change)
426 {
427 	ECacheOfflineChange *chng = change;
428 
429 	if (chng) {
430 		g_free (chng->uid);
431 		g_free (chng->revision);
432 		g_free (chng->object);
433 		g_slice_free (ECacheOfflineChange, chng);
434 	}
435 }
436 
437 /**
438  * e_cache_column_info_new:
439  * @name: a column name
440  * @type: a column type
441  * @index_name: (nullable): an index name for this column, or %NULL
442  *
443  * Returns: (transfer full): A new #ECacheColumnInfo. Free it with
444  *    e_cache_column_info_free() when no longer needed.
445  *
446  * Since: 3.26
447  **/
448 ECacheColumnInfo *
e_cache_column_info_new(const gchar * name,const gchar * type,const gchar * index_name)449 e_cache_column_info_new (const gchar *name,
450 			 const gchar *type,
451 			 const gchar *index_name)
452 {
453 	ECacheColumnInfo *info;
454 
455 	g_return_val_if_fail (name != NULL, NULL);
456 	g_return_val_if_fail (type != NULL, NULL);
457 
458 	info = g_slice_new0 (ECacheColumnInfo);
459 	info->name = g_strdup (name);
460 	info->type = g_strdup (type);
461 	info->index_name = g_strdup (index_name);
462 
463 	return info;
464 }
465 
466 /**
467  * e_cache_column_info_copy:
468  * @info: (nullable): a source #ECacheColumnInfo to copy, or %NULL
469  *
470  * Returns: (transfer full): Copy of the given @info. Free it with
471  *    e_cache_column_info_free() when no longer needed.
472  *    If the @info is %NULL, then returns %NULL as well.
473  *
474  * Since: 3.26
475  **/
476 ECacheColumnInfo *
e_cache_column_info_copy(const ECacheColumnInfo * info)477 e_cache_column_info_copy (const ECacheColumnInfo *info)
478 {
479 	if (!info)
480 		return NULL;
481 
482 	return e_cache_column_info_new (info->name, info->type, info->index_name);
483 }
484 
485 /**
486  * e_cache_column_info_free:
487  * @info: (nullable): an #ECacheColumnInfo
488  *
489  * Frees the @info structure, previously allocated with e_cache_column_info_new()
490  * or e_cache_column_info_copy().
491  *
492  * Since: 3.26
493  **/
494 void
e_cache_column_info_free(gpointer info)495 e_cache_column_info_free (gpointer info)
496 {
497 	ECacheColumnInfo *nfo = info;
498 
499 	if (nfo) {
500 		g_free (nfo->name);
501 		g_free (nfo->type);
502 		g_free (nfo->index_name);
503 		g_slice_free (ECacheColumnInfo, nfo);
504 	}
505 }
506 
507 #define E_CACHE_SET_ERROR_FROM_SQLITE(error, code, message, stmt) \
508 	G_STMT_START { \
509 		if (code == SQLITE_CONSTRAINT) { \
510 			g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_CONSTRAINT, message); \
511 		} else if (code == SQLITE_ABORT || code == SQLITE_INTERRUPT) { \
512 			g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation cancelled: %s", message); \
513 		} else { \
514 			gchar *valid_utf8 = e_util_utf8_make_valid (stmt); \
515 			g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_ENGINE, \
516 				"SQLite error code '%d': %s (statement:%s)", code, message, valid_utf8 ? valid_utf8 : stmt); \
517 			g_free (valid_utf8); \
518 		} \
519 	} G_STMT_END
520 
521 struct CacheSQLiteExecData {
522 	ECache *cache;
523 	ECacheSelectFunc callback;
524 	gpointer user_data;
525 };
526 
527 static gint
e_cache_sqlite_exec_cb(gpointer user_data,gint ncols,gchar ** column_values,gchar ** column_names)528 e_cache_sqlite_exec_cb (gpointer user_data,
529 			gint ncols,
530 			gchar **column_values,
531 			gchar **column_names)
532 {
533 	struct CacheSQLiteExecData *cse = user_data;
534 
535 	g_return_val_if_fail (cse != NULL, SQLITE_MISUSE);
536 	g_return_val_if_fail (cse->callback != NULL, SQLITE_MISUSE);
537 
538 	if (!cse->callback (cse->cache, ncols, (const gchar **) column_names, (const gchar **) column_values, cse->user_data))
539 		return SQLITE_ABORT;
540 
541 	return SQLITE_OK;
542 }
543 
544 static gboolean
e_cache_sqlite_exec_internal(ECache * cache,const gchar * stmt,ECacheSelectFunc callback,gpointer user_data,GCancellable * cancellable,GError ** error)545 e_cache_sqlite_exec_internal (ECache *cache,
546 			      const gchar *stmt,
547 			      ECacheSelectFunc callback,
548 			      gpointer user_data,
549 			      GCancellable *cancellable,
550 			      GError **error)
551 {
552 	struct CacheSQLiteExecData cse;
553 	GCancellable *previous_cancellable;
554 	gchar *errmsg = NULL;
555 	gint ret = -1, retries = 0;
556 
557 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
558 	g_return_val_if_fail (stmt != NULL, FALSE);
559 
560 	g_rec_mutex_lock (&cache->priv->lock);
561 
562 	previous_cancellable = cache->priv->cancellable;
563 	if (cancellable)
564 		cache->priv->cancellable = cancellable;
565 
566 	cse.cache = cache;
567 	cse.callback = callback;
568 	cse.user_data = user_data;
569 
570 	ret = sqlite3_exec (cache->priv->db, stmt, callback ? e_cache_sqlite_exec_cb : NULL, &cse, &errmsg);
571 
572 	while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
573 		/* try for ~15 seconds, then give up */
574 		if (retries > 150)
575 			break;
576 		retries++;
577 
578 		g_clear_pointer (&errmsg, sqlite3_free);
579 		g_thread_yield ();
580 		g_usleep (100 * 1000); /* Sleep for 100 ms */
581 
582 		ret = sqlite3_exec (cache->priv->db, stmt, callback ? e_cache_sqlite_exec_cb : NULL, &cse, &errmsg);
583 	}
584 
585 	cache->priv->cancellable = previous_cancellable;
586 
587 	g_rec_mutex_unlock (&cache->priv->lock);
588 
589 	if (ret != SQLITE_OK) {
590 		E_CACHE_SET_ERROR_FROM_SQLITE (error, ret, errmsg, stmt);
591 		sqlite3_free (errmsg);
592 		return FALSE;
593 	}
594 
595 	if (errmsg)
596 		sqlite3_free (errmsg);
597 
598 	return TRUE;
599 }
600 
601 static gboolean
e_cache_sqlite_exec_printf(ECache * cache,const gchar * format,ECacheSelectFunc callback,gpointer user_data,GCancellable * cancellable,GError ** error,...)602 e_cache_sqlite_exec_printf (ECache *cache,
603 			    const gchar *format,
604 			    ECacheSelectFunc callback,
605 			    gpointer user_data,
606 			    GCancellable *cancellable,
607 			    GError **error,
608 			    ...)
609 {
610 	gboolean success;
611 	va_list args;
612 	gchar *stmt;
613 
614 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
615 	g_return_val_if_fail (format != NULL, FALSE);
616 
617 	va_start (args, error);
618 	stmt = sqlite3_vmprintf (format, args);
619 
620 	success = e_cache_sqlite_exec_internal (cache, stmt, callback, user_data, cancellable, error);
621 
622 	sqlite3_free (stmt);
623 	va_end (args);
624 
625 	return success;
626 }
627 
628 static gboolean
e_cache_read_key_value(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)629 e_cache_read_key_value (ECache *cache,
630 			gint ncols,
631 			const gchar **column_names,
632 			const gchar **column_values,
633 			gpointer user_data)
634 {
635 	gchar **pvalue = user_data;
636 
637 	g_return_val_if_fail (ncols == 1, FALSE);
638 	g_return_val_if_fail (column_names != NULL, FALSE);
639 	g_return_val_if_fail (column_values != NULL, FALSE);
640 	g_return_val_if_fail (pvalue != NULL, FALSE);
641 
642 	if (!*pvalue)
643 		*pvalue = g_strdup (column_values[0]);
644 
645 	return TRUE;
646 }
647 
648 static gchar *
e_cache_build_user_key(const gchar * key)649 e_cache_build_user_key (const gchar *key)
650 {
651 	return g_strconcat ("user::", key, NULL);
652 }
653 
654 static gboolean
e_cache_set_key_internal(ECache * cache,gboolean is_user_key,const gchar * key,const gchar * value,GError ** error)655 e_cache_set_key_internal (ECache *cache,
656 			  gboolean is_user_key,
657 			  const gchar *key,
658 			  const gchar *value,
659 			  GError **error)
660 {
661 	gchar *tmp = NULL;
662 	const gchar *usekey;
663 	gboolean success;
664 
665 	if (is_user_key) {
666 		tmp = e_cache_build_user_key (key);
667 		usekey = tmp;
668 	} else {
669 		usekey = key;
670 	}
671 
672 	if (value) {
673 		success = e_cache_sqlite_exec_printf (cache,
674 			"INSERT or REPLACE INTO " E_CACHE_TABLE_KEYS " (key, value) VALUES (%Q, %Q)",
675 			NULL, NULL, NULL, error,
676 			usekey, value);
677 	} else {
678 		success = e_cache_sqlite_exec_printf (cache,
679 			"DELETE FROM " E_CACHE_TABLE_KEYS " WHERE key = %Q",
680 			NULL, NULL, NULL, error,
681 			usekey);
682 	}
683 
684 	g_free (tmp);
685 
686 	return success;
687 }
688 
689 static gchar *
e_cache_dup_key_internal(ECache * cache,gboolean is_user_key,const gchar * key,GError ** error)690 e_cache_dup_key_internal (ECache *cache,
691 			  gboolean is_user_key,
692 			  const gchar *key,
693 			  GError **error)
694 {
695 	gchar *tmp = NULL;
696 	const gchar *usekey;
697 	gchar *value = NULL;
698 
699 	if (is_user_key) {
700 		tmp = e_cache_build_user_key (key);
701 		usekey = tmp;
702 	} else {
703 		usekey = key;
704 	}
705 
706 	if (!e_cache_sqlite_exec_printf (cache,
707 		"SELECT value FROM " E_CACHE_TABLE_KEYS " WHERE key = %Q",
708 		e_cache_read_key_value, &value, NULL, error,
709 		usekey)) {
710 		g_warn_if_fail (value == NULL);
711 	}
712 
713 	g_free (tmp);
714 
715 	return value;
716 }
717 
718 static gint
e_cache_check_cancelled_cb(gpointer user_data)719 e_cache_check_cancelled_cb (gpointer user_data)
720 {
721 	ECache *cache = user_data;
722 
723 	/* Do not use E_IS_CACHE() here, for performance reasons */
724 	g_return_val_if_fail (cache != NULL, SQLITE_ABORT);
725 
726 	if (cache->priv->cancellable &&
727 	    g_cancellable_is_cancelled (cache->priv->cancellable)) {
728 		return SQLITE_ABORT;
729 	}
730 
731 	return SQLITE_OK;
732 }
733 
734 static gboolean
e_cache_init_sqlite(ECache * cache,const gchar * filename,GCancellable * cancellable,GError ** error)735 e_cache_init_sqlite (ECache *cache,
736 		     const gchar *filename,
737 		     GCancellable *cancellable,
738 		     GError **error)
739 {
740 	gint ret;
741 
742 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
743 	g_return_val_if_fail (filename != NULL, FALSE);
744 	g_return_val_if_fail (cache->priv->filename == NULL, FALSE);
745 
746 	cache->priv->filename = g_strdup (filename);
747 
748 	ret = sqlite3_open (filename, &cache->priv->db);
749 	if (ret != SQLITE_OK) {
750 		if (!cache->priv->db) {
751 			g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_LOAD, _("Out of memory"));
752 		} else {
753 			const gchar *errmsg = sqlite3_errmsg (cache->priv->db);
754 
755 			g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_ENGINE,
756 				_("Can’t open database %s: %s"), filename, errmsg);
757 
758 			sqlite3_close (cache->priv->db);
759 			cache->priv->db = NULL;
760 		}
761 
762 		return FALSE;
763 	}
764 
765 	/* Handle GCancellable */
766 	sqlite3_progress_handler (
767 		cache->priv->db,
768 		E_CACHE_CANCEL_BATCH_SIZE,
769 		e_cache_check_cancelled_cb,
770 		cache);
771 
772 	return e_cache_sqlite_exec_internal (cache, "ATTACH DATABASE ':memory:' AS mem", NULL, NULL, cancellable, error) &&
773 		e_cache_sqlite_exec_internal (cache, "PRAGMA foreign_keys = ON",          NULL, NULL, cancellable, error) &&
774 		e_cache_sqlite_exec_internal (cache, "PRAGMA case_sensitive_like = ON",   NULL, NULL, cancellable, error);
775 }
776 
777 static gboolean
e_cache_garther_column_names_cb(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)778 e_cache_garther_column_names_cb (ECache *cache,
779 				 gint ncols,
780 				 const gchar *column_names[],
781 				 const gchar *column_values[],
782 				 gpointer user_data)
783 {
784 	GHashTable *known_columns = user_data;
785 	gint ii;
786 
787 	g_return_val_if_fail (known_columns != NULL, FALSE);
788 	g_return_val_if_fail (column_names != NULL, FALSE);
789 	g_return_val_if_fail (column_values != NULL, FALSE);
790 
791 	for (ii = 0; ii < ncols; ii++) {
792 		if (column_names[ii] && camel_strcase_equal (column_names[ii], "name")) {
793 			if (column_values[ii])
794 				g_hash_table_insert (known_columns, g_strdup (column_values[ii]), NULL);
795 			break;
796 		}
797 	}
798 
799 	return TRUE;
800 }
801 
802 static gboolean
e_cache_init_tables(ECache * cache,const GSList * other_columns,GCancellable * cancellable,GError ** error)803 e_cache_init_tables (ECache *cache,
804 		     const GSList *other_columns,
805 		     GCancellable *cancellable,
806 		     GError **error)
807 {
808 	GHashTable *known_columns;
809 	GString *objects_stmt;
810 	const GSList *link;
811 
812 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
813 	g_return_val_if_fail (cache->priv->db != NULL, FALSE);
814 
815 	if (!e_cache_sqlite_exec_internal (cache,
816 		"CREATE TABLE IF NOT EXISTS " E_CACHE_TABLE_KEYS " ("
817 		"key TEXT PRIMARY KEY,"
818 		"value TEXT)",
819 		NULL, NULL, cancellable, error)) {
820 		return FALSE;
821 	}
822 
823 	objects_stmt = g_string_new ("");
824 
825 	g_string_append (objects_stmt, "CREATE TABLE IF NOT EXISTS " E_CACHE_TABLE_OBJECTS " ("
826 		E_CACHE_COLUMN_UID " TEXT PRIMARY KEY,"
827 		E_CACHE_COLUMN_REVISION " TEXT,"
828 		E_CACHE_COLUMN_OBJECT " TEXT,"
829 		E_CACHE_COLUMN_STATE " INTEGER");
830 
831 	for (link = other_columns; link; link = g_slist_next (link)) {
832 		const ECacheColumnInfo *info = link->data;
833 
834 		if (!info)
835 			continue;
836 
837 		g_string_append_c (objects_stmt, ',');
838 		g_string_append (objects_stmt, info->name);
839 		g_string_append_c (objects_stmt, ' ');
840 		g_string_append (objects_stmt, info->type);
841 	}
842 
843 	g_string_append_c (objects_stmt, ')');
844 
845 	if (!e_cache_sqlite_exec_internal (cache, objects_stmt->str, NULL, NULL, cancellable, error)) {
846 		g_string_free (objects_stmt, TRUE);
847 
848 		return FALSE;
849 	}
850 
851 	g_string_free (objects_stmt, TRUE);
852 
853 	/* Verify that all other columns are there and remove those unused */
854 	known_columns = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
855 
856 	if (!e_cache_sqlite_exec_internal (cache, "PRAGMA table_info (" E_CACHE_TABLE_OBJECTS ")",
857 		e_cache_garther_column_names_cb, known_columns, cancellable, error)) {
858 		g_string_free (objects_stmt, TRUE);
859 
860 		return FALSE;
861 	}
862 
863 	g_hash_table_remove (known_columns, E_CACHE_COLUMN_UID);
864 	g_hash_table_remove (known_columns, E_CACHE_COLUMN_REVISION);
865 	g_hash_table_remove (known_columns, E_CACHE_COLUMN_OBJECT);
866 	g_hash_table_remove (known_columns, E_CACHE_COLUMN_STATE);
867 
868 	for (link = other_columns; link; link = g_slist_next (link)) {
869 		const ECacheColumnInfo *info = link->data;
870 
871 		if (!info)
872 			continue;
873 
874 		if (g_hash_table_remove (known_columns, info->name))
875 			continue;
876 
877 		if (!e_cache_sqlite_exec_printf (cache,
878 			"ALTER TABLE " E_CACHE_TABLE_OBJECTS " ADD COLUMN %Q %s",
879 			NULL, NULL, cancellable, error,
880 			info->name, info->type)) {
881 			g_hash_table_destroy (known_columns);
882 
883 			return FALSE;
884 		}
885 	}
886 
887 	g_hash_table_destroy (known_columns);
888 
889 	for (link = other_columns; link; link = g_slist_next (link)) {
890 		const ECacheColumnInfo *info = link->data;
891 
892 		if (!info || !info->index_name)
893 			continue;
894 
895 		if (!e_cache_sqlite_exec_printf (cache,
896 			"CREATE INDEX IF NOT EXISTS %Q ON " E_CACHE_TABLE_OBJECTS " (%s)",
897 			NULL, NULL, cancellable, error,
898 			info->index_name, info->name)) {
899 			return FALSE;
900 		}
901 	}
902 
903 	return TRUE;
904 }
905 
906 /**
907  * e_cache_initialize_sync:
908  * @cache: an #ECache
909  * @filename: a filename of an SQLite database to use
910  * @other_columns: (element-type ECacheColumnInfo) (nullable): an optional
911  *    #GSList with additional columns to add to the objects table
912  * @cancellable: optional #GCancellable object, or %NULL
913  * @error: return location for a #GError, or %NULL
914  *
915  * Initializes the @cache and opens the @filename database.
916  * This should be called by the descendant.
917  *
918  * The @other_columns are added to the objects table (@E_CACHE_TABLE_OBJECTS).
919  * Values for these columns are returned by e_cache_get()
920  * and can be stored with e_cache_put().
921  *
922  * Returns: Whether succeeded.
923  *
924  * Since: 3.26
925  **/
926 gboolean
e_cache_initialize_sync(ECache * cache,const gchar * filename,const GSList * other_columns,GCancellable * cancellable,GError ** error)927 e_cache_initialize_sync (ECache *cache,
928 			 const gchar *filename,
929 			 const GSList *other_columns,
930 			 GCancellable *cancellable,
931 			 GError **error)
932 {
933 	gchar *dirname;
934 	gboolean success;
935 
936 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
937 	g_return_val_if_fail (cache->priv->filename == NULL, FALSE);
938 
939 	/* Ensure existance of the directories leading up to 'filename' */
940 	dirname = g_path_get_dirname (filename);
941 	if (g_mkdir_with_parents (dirname, 0777) < 0) {
942 		g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_LOAD,
943 			_("Can not make parent directory: %s"),
944 			g_strerror (errno));
945 		g_free (dirname);
946 
947 		return FALSE;
948 	}
949 
950 	g_free (dirname);
951 
952 	g_rec_mutex_lock (&cache->priv->lock);
953 
954 	success = e_cache_init_sqlite (cache, filename, cancellable, error) &&
955 		e_cache_init_tables (cache, other_columns, cancellable, error);
956 
957 	g_rec_mutex_unlock (&cache->priv->lock);
958 
959 	return success;
960 }
961 
962 /**
963  * e_cache_get_filename:
964  * @cache: an #ECache
965  *
966  * Returns: a filename of the @cache, with which it had been initialized.
967  *
968  * Since: 3.26
969  **/
970 const gchar *
e_cache_get_filename(ECache * cache)971 e_cache_get_filename (ECache *cache)
972 {
973 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
974 
975 	return cache->priv->filename;
976 }
977 
978 /**
979  * e_cache_get_version:
980  * @cache: an #ECache
981  *
982  * Returns: A cache data version. This is meant to be used by the descendants.
983  *
984  * Since: 3.26
985  **/
986 gint
e_cache_get_version(ECache * cache)987 e_cache_get_version (ECache *cache)
988 {
989 	gchar *value;
990 	gint version = -1;
991 
992 	g_return_val_if_fail (E_IS_CACHE (cache), -1);
993 
994 	value = e_cache_dup_key_internal (cache, FALSE, E_CACHE_KEY_VERSION, NULL);
995 
996 	if (value) {
997 		version = g_ascii_strtoll (value, NULL, 10);
998 		g_free (value);
999 	}
1000 
1001 	return version;
1002 }
1003 
1004 /**
1005  * e_cache_set_version:
1006  * @cache: an #ECache
1007  * @version: a cache data version to set
1008  *
1009  * Sets a cache data version. This is meant to be used by the descendants.
1010  * The @version should be greater than zero.
1011  *
1012  * Since: 3.26
1013  **/
1014 void
e_cache_set_version(ECache * cache,gint version)1015 e_cache_set_version (ECache *cache,
1016 		     gint version)
1017 {
1018 	gchar *value;
1019 
1020 	g_return_if_fail (E_IS_CACHE (cache));
1021 	g_return_if_fail (version > 0);
1022 
1023 	value = g_strdup_printf ("%d", version);
1024 	e_cache_set_key_internal (cache, FALSE, E_CACHE_KEY_VERSION, value, NULL);
1025 	g_free (value);
1026 }
1027 
1028 /**
1029  * e_cache_dup_revision:
1030  * @cache: an #ECache
1031  *
1032  * Returns: (transfer full): A revision of the whole @cache. This is meant to be
1033  *    used by the descendants. Free the returned pointer with g_free(), when no
1034  *    longer needed.
1035  *
1036  * Since: 3.26
1037  **/
1038 gchar *
e_cache_dup_revision(ECache * cache)1039 e_cache_dup_revision (ECache *cache)
1040 {
1041 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1042 
1043 	return e_cache_dup_key_internal (cache, FALSE, E_CACHE_KEY_REVISION, NULL);
1044 }
1045 
1046 /**
1047  * e_cache_set_revision:
1048  * @cache: an #ECache
1049  * @revision: (nullable): a revision to set; use %NULL to unset it
1050  *
1051  * Sets the @revision of the whole @cache. This is not meant to be
1052  * used by the descendants, because the revision is updated automatically
1053  * when needed. The descendants can listen to "revision-changed" signal.
1054  *
1055  * Since: 3.26
1056  **/
1057 void
e_cache_set_revision(ECache * cache,const gchar * revision)1058 e_cache_set_revision (ECache *cache,
1059 		      const gchar *revision)
1060 {
1061 	g_return_if_fail (E_IS_CACHE (cache));
1062 
1063 	e_cache_set_key_internal (cache, FALSE, E_CACHE_KEY_REVISION, revision, NULL);
1064 
1065 	g_signal_emit (cache, signals[REVISION_CHANGED], 0, NULL);
1066 }
1067 
1068 /**
1069  * e_cache_change_revision:
1070  * @cache: an #ECache
1071  *
1072  * Instructs the @cache to change its revision. In case the revision
1073  * change is frozen with e_cache_freeze_revision_change() it notes to
1074  * change the revision once the revision change is fully thaw.
1075  *
1076  * Since: 3.26
1077  **/
1078 void
e_cache_change_revision(ECache * cache)1079 e_cache_change_revision (ECache *cache)
1080 {
1081 	g_return_if_fail (E_IS_CACHE (cache));
1082 
1083 	g_rec_mutex_lock (&cache->priv->lock);
1084 
1085 	if (e_cache_is_revision_change_frozen (cache)) {
1086 		cache->priv->needs_revision_change = TRUE;
1087 	} else {
1088 		gchar time_string[100] = { 0 };
1089 		const struct tm *tm = NULL;
1090 		time_t t;
1091 		gint64 revision_time;
1092 		gchar *revision;
1093 
1094 		revision_time = g_get_real_time () / (1000 * 1000);
1095 		t = (time_t) revision_time;
1096 
1097 		if (revision_time != cache->priv->last_revision_time) {
1098 			cache->priv->revision_counter = 0;
1099 			cache->priv->last_revision_time = revision_time;
1100 		}
1101 
1102 		tm = gmtime (&t);
1103 		if (tm)
1104 			strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
1105 
1106 		revision = g_strdup_printf ("%s(%d)", time_string, cache->priv->revision_counter++);
1107 
1108 		e_cache_set_revision (cache, revision);
1109 
1110 		g_free (revision);
1111 	}
1112 
1113 	g_rec_mutex_unlock (&cache->priv->lock);
1114 }
1115 
1116 /**
1117  * e_cache_freeze_revision_change:
1118  * @cache: an #ECache
1119  *
1120  * Freezes automatic revision change for the @cache. The function
1121  * can be called multiple times, but each such call requires its
1122  * pair function e_cache_thaw_revision_change() call. See also
1123  * e_cache_change_revision().
1124  *
1125  * Since: 3.26
1126  **/
1127 void
e_cache_freeze_revision_change(ECache * cache)1128 e_cache_freeze_revision_change (ECache *cache)
1129 {
1130 	g_return_if_fail (E_IS_CACHE (cache));
1131 
1132 	g_rec_mutex_lock (&cache->priv->lock);
1133 
1134 	cache->priv->revision_change_frozen++;
1135 	g_warn_if_fail (cache->priv->revision_change_frozen != 0);
1136 
1137 	g_rec_mutex_unlock (&cache->priv->lock);
1138 }
1139 
1140 /**
1141  * e_cache_thaw_revision_change:
1142  * @cache: an #ECache
1143  *
1144  * Thaws automatic revision change for the @cache. It's the pair
1145  * function of e_cache_freeze_revision_change().
1146  *
1147  * Since: 3.26
1148  **/
1149 void
e_cache_thaw_revision_change(ECache * cache)1150 e_cache_thaw_revision_change (ECache *cache)
1151 {
1152 	g_return_if_fail (E_IS_CACHE (cache));
1153 
1154 	g_rec_mutex_lock (&cache->priv->lock);
1155 
1156 	if (!cache->priv->revision_change_frozen) {
1157 		g_warn_if_fail (cache->priv->revision_change_frozen > 0);
1158 	} else {
1159 		cache->priv->revision_change_frozen--;
1160 		if (!cache->priv->revision_change_frozen &&
1161 		    cache->priv->needs_revision_change) {
1162 			cache->priv->needs_revision_change = FALSE;
1163 			e_cache_change_revision (cache);
1164 		}
1165 	}
1166 
1167 	g_rec_mutex_unlock (&cache->priv->lock);
1168 }
1169 
1170 /**
1171  * e_cache_is_revision_change_frozen:
1172  * @cache: an #ECache
1173  *
1174  * Returns: Whether automatic revision change for the @cache
1175  *    is currently frozen.
1176  *
1177  * Since: 3.26
1178  **/
1179 gboolean
e_cache_is_revision_change_frozen(ECache * cache)1180 e_cache_is_revision_change_frozen (ECache *cache)
1181 {
1182 	gboolean frozen;
1183 
1184 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1185 
1186 	g_rec_mutex_lock (&cache->priv->lock);
1187 	frozen = cache->priv->revision_change_frozen > 0;
1188 	g_rec_mutex_unlock (&cache->priv->lock);
1189 
1190 	return frozen;
1191 }
1192 
1193 /**
1194  * e_cache_erase:
1195  * @cache: an #ECache
1196  *
1197  * Erases the cache and all of its content from the disk.
1198  * The only valid operation after this is to free the @cache.
1199  *
1200  * Since: 3.26
1201  **/
1202 void
e_cache_erase(ECache * cache)1203 e_cache_erase (ECache *cache)
1204 {
1205 	ECacheClass *klass;
1206 
1207 	g_return_if_fail (E_IS_CACHE (cache));
1208 
1209 	if (!cache->priv->db)
1210 		return;
1211 
1212 	klass = E_CACHE_GET_CLASS (cache);
1213 	g_return_if_fail (klass != NULL);
1214 
1215 	if (klass->erase)
1216 		klass->erase (cache);
1217 
1218 	sqlite3_close (cache->priv->db);
1219 	cache->priv->db = NULL;
1220 
1221 	g_unlink (cache->priv->filename);
1222 
1223 	g_free (cache->priv->filename);
1224 	cache->priv->filename = NULL;
1225 }
1226 
1227 static gboolean
e_cache_count_rows_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1228 e_cache_count_rows_cb (ECache *cache,
1229 		       gint ncols,
1230 		       const gchar **column_names,
1231 		       const gchar **column_values,
1232 		       gpointer user_data)
1233 {
1234 	guint *pnrows = user_data;
1235 
1236 	g_return_val_if_fail (pnrows != NULL, FALSE);
1237 
1238 	*pnrows = (*pnrows) + 1;
1239 
1240 	return TRUE;
1241 }
1242 
1243 /**
1244  * e_cache_contains:
1245  * @cache: an #ECache
1246  * @uid: a unique identifier of an object
1247  * @deleted_flag: one of #ECacheDeletedFlag enum
1248  *
1249  * Checkes whether the @cache contains an object with
1250  * the given @uid.
1251  *
1252  * Returns: Whether the object had been found.
1253  *
1254  * Since: 3.26
1255  **/
1256 gboolean
e_cache_contains(ECache * cache,const gchar * uid,ECacheDeletedFlag deleted_flag)1257 e_cache_contains (ECache *cache,
1258 		  const gchar *uid,
1259 		  ECacheDeletedFlag deleted_flag)
1260 {
1261 	guint nrows = 0;
1262 
1263 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1264 	g_return_val_if_fail (uid != NULL, FALSE);
1265 
1266 	if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
1267 		e_cache_sqlite_exec_printf (cache,
1268 			"SELECT " E_CACHE_COLUMN_UID " FROM " E_CACHE_TABLE_OBJECTS
1269 			" WHERE " E_CACHE_COLUMN_UID " = %Q"
1270 			" LIMIT 2",
1271 			e_cache_count_rows_cb, &nrows, NULL, NULL,
1272 			uid);
1273 	} else {
1274 		e_cache_sqlite_exec_printf (cache,
1275 			"SELECT " E_CACHE_COLUMN_UID " FROM " E_CACHE_TABLE_OBJECTS
1276 			" WHERE " E_CACHE_COLUMN_UID " = %Q AND " E_CACHE_COLUMN_STATE " != %d"
1277 			" LIMIT 2",
1278 			e_cache_count_rows_cb, &nrows, NULL, NULL,
1279 			uid, E_OFFLINE_STATE_LOCALLY_DELETED);
1280 	}
1281 
1282 	g_warn_if_fail (nrows <= 1);
1283 
1284 	return nrows > 0;
1285 }
1286 
1287 struct GetObjectData {
1288 	gchar *object;
1289 	gchar **out_revision;
1290 	ECacheColumnValues **out_other_columns;
1291 };
1292 
1293 static gboolean
e_cache_get_object_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1294 e_cache_get_object_cb (ECache *cache,
1295 		       gint ncols,
1296 		       const gchar **column_names,
1297 		       const gchar **column_values,
1298 		       gpointer user_data)
1299 {
1300 	struct GetObjectData *gd = user_data;
1301 	gint ii;
1302 
1303 	g_return_val_if_fail (gd != NULL, FALSE);
1304 	g_return_val_if_fail (column_names != NULL, FALSE);
1305 	g_return_val_if_fail (column_values != NULL, FALSE);
1306 
1307 	for (ii = 0; ii < ncols; ii++) {
1308 		if (g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) == 0 ||
1309 		    g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) == 0) {
1310 			/* Skip these two */
1311 		} else if (g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) == 0) {
1312 			if (gd->out_revision)
1313 				*gd->out_revision = g_strdup (column_values[ii]);
1314 		} else if (g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) == 0) {
1315 			gd->object = g_strdup (column_values[ii]);
1316 		} else if (gd->out_other_columns) {
1317 			if (!*gd->out_other_columns)
1318 				*gd->out_other_columns = e_cache_column_values_new ();
1319 
1320 			e_cache_column_values_put (*gd->out_other_columns, column_names[ii], column_values[ii]);
1321 		} else if (gd->object && (!gd->out_revision || *gd->out_revision)) {
1322 			/* Short-break the cycle when the other columns are not requested and
1323 			   the object/revision values were already read. */
1324 			break;
1325 		}
1326 	}
1327 
1328 	return TRUE;
1329 }
1330 
1331 static gchar *
e_cache_get_object_internal(ECache * cache,gboolean include_deleted,const gchar * uid,gchar ** out_revision,ECacheColumnValues ** out_other_columns,GCancellable * cancellable,GError ** error)1332 e_cache_get_object_internal (ECache *cache,
1333 			     gboolean include_deleted,
1334 			     const gchar *uid,
1335 			     gchar **out_revision,
1336 			     ECacheColumnValues **out_other_columns,
1337 			     GCancellable *cancellable,
1338 			     GError **error)
1339 {
1340 	struct GetObjectData gd;
1341 	gboolean success;
1342 
1343 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1344 	g_return_val_if_fail (uid != NULL, NULL);
1345 
1346 	if (out_revision)
1347 		*out_revision = NULL;
1348 
1349 	if (out_other_columns)
1350 		*out_other_columns = NULL;
1351 
1352 	gd.object = NULL;
1353 	gd.out_revision = out_revision;
1354 	gd.out_other_columns = out_other_columns;
1355 
1356 	if (include_deleted) {
1357 		success = e_cache_sqlite_exec_printf (cache,
1358 			"SELECT * FROM " E_CACHE_TABLE_OBJECTS
1359 			" WHERE " E_CACHE_COLUMN_UID " = %Q",
1360 			e_cache_get_object_cb, &gd, cancellable, error,
1361 			uid);
1362 	} else {
1363 		success = e_cache_sqlite_exec_printf (cache,
1364 			"SELECT * FROM " E_CACHE_TABLE_OBJECTS
1365 			" WHERE " E_CACHE_COLUMN_UID " = %Q AND " E_CACHE_COLUMN_STATE " != %d",
1366 			e_cache_get_object_cb, &gd, cancellable, error,
1367 			uid, E_OFFLINE_STATE_LOCALLY_DELETED);
1368 	}
1369 
1370 	if (success && !gd.object)
1371 		g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object “%s” not found"), uid);
1372 
1373 	return gd.object;
1374 }
1375 
1376 /**
1377  * e_cache_get:
1378  * @cache: an #ECache
1379  * @uid: a unique identifier of an object
1380  * @out_revision: (out) (nullable) (transfer full): an out variable for a revision
1381  *    of the object, or %NULL to ignore
1382  * @out_other_columns: (out) (nullable) (transfer full): an out
1383  *    variable for #ECacheColumnValues other columns, as defined when creating the @cache, or %NULL to ignore
1384  * @cancellable: optional #GCancellable object, or %NULL
1385  * @error: return location for a #GError, or %NULL
1386  *
1387  * Returns an object with the given @uid. This function does not consider locally
1388  * deleted objects. The @out_revision is set to the object revision, if not %NULL.
1389  * Free it with g_free() when no longer needed. Similarly the @out_other_columns
1390  * contains a column name to column value strings for additional columns which had
1391  * been requested when calling e_cache_initialize_sync(), if not %NULL.
1392  * Free the returned #ECacheColumnValues with e_cache_column_values_free(), when
1393  * no longer needed.
1394  *
1395  * Returns: (nullable) (transfer full): An object with the given @uid. Free it
1396  *    with g_free(), when no longer needed. Returns %NULL on error, like when
1397  *    the object could not be found.
1398  *
1399  * Since: 3.26
1400  **/
1401 gchar *
e_cache_get(ECache * cache,const gchar * uid,gchar ** out_revision,ECacheColumnValues ** out_other_columns,GCancellable * cancellable,GError ** error)1402 e_cache_get (ECache *cache,
1403 	     const gchar *uid,
1404 	     gchar **out_revision,
1405 	     ECacheColumnValues **out_other_columns,
1406 	     GCancellable *cancellable,
1407 	     GError **error)
1408 {
1409 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1410 	g_return_val_if_fail (uid != NULL, NULL);
1411 
1412 	return e_cache_get_object_internal (cache, FALSE, uid, out_revision, out_other_columns, cancellable, error);
1413 }
1414 
1415 /**
1416  * e_cache_get_object_include_deleted:
1417  * @cache: an #ECache
1418  * @uid: a unique identifier of an object
1419  * @out_revision: (out) (nullable) (transfer full): an out variable for a revision
1420  *    of the object, or %NULL to ignore
1421  * @out_other_columns: (out) (nullable) (transfer full): an out
1422  *    variable for #ECacheColumnValues other columns, as defined when creating the @cache, or %NULL to ignore
1423  * @cancellable: optional #GCancellable object, or %NULL
1424  * @error: return location for a #GError, or %NULL
1425  *
1426  * The same as e_cache_get(), only considers also locally deleted objects.
1427  *
1428  * Returns: (nullable) (transfer full): An object with the given @uid. Free it
1429  *    with g_free(), when no longer needed. Returns %NULL on error, like when
1430  *    the object could not be found.
1431  *
1432  * Since: 3.30
1433  **/
1434 gchar *
e_cache_get_object_include_deleted(ECache * cache,const gchar * uid,gchar ** out_revision,ECacheColumnValues ** out_other_columns,GCancellable * cancellable,GError ** error)1435 e_cache_get_object_include_deleted (ECache *cache,
1436 				    const gchar *uid,
1437 				    gchar **out_revision,
1438 				    ECacheColumnValues **out_other_columns,
1439 				    GCancellable *cancellable,
1440 				    GError **error)
1441 {
1442 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1443 	g_return_val_if_fail (uid != NULL, NULL);
1444 
1445 	return e_cache_get_object_internal (cache, TRUE, uid, out_revision, out_other_columns, cancellable, error);
1446 }
1447 
1448 static gboolean
e_cache_put_locked(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,EOfflineState offline_state,gboolean is_replace,GCancellable * cancellable,GError ** error)1449 e_cache_put_locked (ECache *cache,
1450 		    const gchar *uid,
1451 		    const gchar *revision,
1452 		    const gchar *object,
1453 		    ECacheColumnValues *other_columns,
1454 		    EOfflineState offline_state,
1455 		    gboolean is_replace,
1456 		    GCancellable *cancellable,
1457 		    GError **error)
1458 {
1459 	ECacheColumnValues *my_other_columns = NULL;
1460 	gboolean success = TRUE;
1461 
1462 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1463 	g_return_val_if_fail (uid != NULL, FALSE);
1464 	g_return_val_if_fail (object != NULL, FALSE);
1465 
1466 	if (!other_columns) {
1467 		my_other_columns = e_cache_column_values_new ();
1468 		other_columns = my_other_columns;
1469 	}
1470 
1471 	g_signal_emit (cache,
1472 		       signals[BEFORE_PUT],
1473 		       0,
1474 		       uid, revision, object, other_columns,
1475 		       is_replace, cancellable, error,
1476 		       &success);
1477 
1478 	if (success) {
1479 		ECacheClass *klass;
1480 
1481 		klass = E_CACHE_GET_CLASS (cache);
1482 		g_return_val_if_fail (klass != NULL, FALSE);
1483 		g_return_val_if_fail (klass->put_locked != NULL, FALSE);
1484 
1485 		success = klass->put_locked (cache, uid, revision, object, other_columns, offline_state, is_replace, cancellable, error);
1486 
1487 		if (success)
1488 			e_cache_change_revision (cache);
1489 	}
1490 
1491 	e_cache_column_values_free (my_other_columns);
1492 
1493 	return success;
1494 }
1495 
1496 /**
1497  * e_cache_put:
1498  * @cache: an #ECache
1499  * @uid: a unique identifier of an object
1500  * @revision: (nullable): a revision of the object
1501  * @object: the object itself
1502  * @other_columns: (nullable): an #ECacheColumnValues with other columns to set; can be %NULL
1503  * @offline_flag: one of #ECacheOfflineFlag, whether putting this object in offline
1504  * @cancellable: optional #GCancellable object, or %NULL
1505  * @error: return location for a #GError, or %NULL
1506  *
1507  * Stores an object into the cache. Depending on @offline_flag, this update
1508  * the object's offline state accordingly. When the @offline_flag is set
1509  * to %E_CACHE_IS_ONLINE, then it's set to #E_OFFLINE_STATE_SYNCED, like
1510  * to be fully synchronized with the server, regardless of its previous
1511  * offline state. Overwriting locally deleted object behaves like an addition
1512  * of a completely new object.
1513  *
1514  * Returns: Whether succeeded.
1515  *
1516  * Since: 3.26
1517  **/
1518 gboolean
e_cache_put(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,ECacheOfflineFlag offline_flag,GCancellable * cancellable,GError ** error)1519 e_cache_put (ECache *cache,
1520 	     const gchar *uid,
1521 	     const gchar *revision,
1522 	     const gchar *object,
1523 	     ECacheColumnValues *other_columns,
1524 	     ECacheOfflineFlag offline_flag,
1525 	     GCancellable *cancellable,
1526 	     GError **error)
1527 {
1528 	EOfflineState offline_state;
1529 	gboolean success = TRUE, is_replace;
1530 
1531 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1532 	g_return_val_if_fail (uid != NULL, FALSE);
1533 	g_return_val_if_fail (object != NULL, FALSE);
1534 
1535 	e_cache_lock (cache, E_CACHE_LOCK_WRITE);
1536 
1537 	if (offline_flag == E_CACHE_IS_ONLINE) {
1538 		is_replace = e_cache_contains (cache, uid, E_CACHE_EXCLUDE_DELETED);
1539 		offline_state = E_OFFLINE_STATE_SYNCED;
1540 	} else {
1541 		is_replace = e_cache_contains (cache, uid, E_CACHE_INCLUDE_DELETED);
1542 		if (is_replace) {
1543 			GError *local_error = NULL;
1544 
1545 			offline_state = e_cache_get_offline_state (cache, uid, cancellable, &local_error);
1546 
1547 			if (local_error) {
1548 				success = FALSE;
1549 				g_propagate_error (error, local_error);
1550 			} else if (offline_state != E_OFFLINE_STATE_LOCALLY_CREATED) {
1551 				offline_state = E_OFFLINE_STATE_LOCALLY_MODIFIED;
1552 			}
1553 		} else {
1554 			offline_state = E_OFFLINE_STATE_LOCALLY_CREATED;
1555 		}
1556 	}
1557 
1558 	success = success && e_cache_put_locked (cache, uid, revision, object, other_columns,
1559 		offline_state, is_replace, cancellable, error);
1560 
1561 	e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
1562 
1563 	return success;
1564 }
1565 
1566 /**
1567  * e_cache_remove:
1568  * @cache: an #ECache
1569  * @uid: a unique identifier of an object
1570  * @offline_flag: one of #ECacheOfflineFlag, whether removing the object in offline
1571  * @cancellable: optional #GCancellable object, or %NULL
1572  * @error: return location for a #GError, or %NULL
1573  *
1574  * Removes the object with the given @uid from the @cache. Based on the @offline_flag,
1575  * it can remove also any information about locally made offline changes. Removing
1576  * the object with %E_CACHE_IS_OFFLINE will still remember it for later use
1577  * with e_cache_get_offline_changes().
1578  *
1579  * Returns: Whether succeeded.
1580  *
1581  * Since: 3.26
1582  **/
1583 gboolean
e_cache_remove(ECache * cache,const gchar * uid,ECacheOfflineFlag offline_flag,GCancellable * cancellable,GError ** error)1584 e_cache_remove (ECache *cache,
1585 		const gchar *uid,
1586 		ECacheOfflineFlag offline_flag,
1587 		GCancellable *cancellable,
1588 		GError **error)
1589 {
1590 	ECacheClass *klass;
1591 	gboolean success = TRUE;
1592 
1593 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1594 	g_return_val_if_fail (uid != NULL, FALSE);
1595 
1596 	klass = E_CACHE_GET_CLASS (cache);
1597 	g_return_val_if_fail (klass != NULL, FALSE);
1598 	g_return_val_if_fail (klass->remove_locked != NULL, FALSE);
1599 
1600 	e_cache_lock (cache, E_CACHE_LOCK_WRITE);
1601 
1602 	if (offline_flag == E_CACHE_IS_ONLINE) {
1603 		success = klass->remove_locked (cache, uid, cancellable, error);
1604 	} else {
1605 		EOfflineState offline_state;
1606 
1607 		offline_state = e_cache_get_offline_state (cache, uid, cancellable, error);
1608 		if (offline_state == E_OFFLINE_STATE_UNKNOWN) {
1609 			success = FALSE;
1610 		} else if (offline_state == E_OFFLINE_STATE_LOCALLY_CREATED) {
1611 			success = klass->remove_locked (cache, uid, cancellable, error);
1612 		} else {
1613 			g_signal_emit (cache,
1614 				       signals[BEFORE_REMOVE],
1615 				       0,
1616 				       uid, cancellable, error,
1617 				       &success);
1618 
1619 			if (success) {
1620 				success = e_cache_set_offline_state (cache, uid,
1621 					E_OFFLINE_STATE_LOCALLY_DELETED, cancellable, error);
1622 			}
1623 		}
1624 	}
1625 
1626 	if (success)
1627 		e_cache_change_revision (cache);
1628 
1629 	e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
1630 
1631 	return success;
1632 }
1633 
1634 /**
1635  * e_cache_remove_all:
1636  * @cache: an #ECache
1637  * @cancellable: optional #GCancellable object, or %NULL
1638  * @error: return location for a #GError, or %NULL
1639  *
1640  * Removes all objects from the @cache in one call.
1641  *
1642  * Returns: Whether succeeded.
1643  *
1644  * Since: 3.26
1645  **/
1646 gboolean
e_cache_remove_all(ECache * cache,GCancellable * cancellable,GError ** error)1647 e_cache_remove_all (ECache *cache,
1648 		    GCancellable *cancellable,
1649 		    GError **error)
1650 {
1651 	ECacheClass *klass;
1652 	GSList *uids = NULL;
1653 	gboolean success;
1654 
1655 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1656 
1657 	klass = E_CACHE_GET_CLASS (cache);
1658 	g_return_val_if_fail (klass != NULL, FALSE);
1659 	g_return_val_if_fail (klass->remove_all_locked != NULL, FALSE);
1660 
1661 	e_cache_lock (cache, E_CACHE_LOCK_WRITE);
1662 
1663 	success = e_cache_get_uids (cache, E_CACHE_INCLUDE_DELETED, &uids, NULL, cancellable, error);
1664 
1665 	if (success && uids)
1666 		success = klass->remove_all_locked (cache, uids, cancellable, error);
1667 
1668 	if (success) {
1669 		e_cache_sqlite_maybe_vacuum (cache, cancellable, NULL);
1670 		e_cache_change_revision (cache);
1671 	}
1672 
1673 	e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
1674 
1675 	g_slist_free_full (uids, g_free);
1676 
1677 	return success;
1678 }
1679 
1680 static gboolean
e_cache_get_uint64_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1681 e_cache_get_uint64_cb (ECache *cache,
1682 		       gint ncols,
1683 		       const gchar **column_names,
1684 		       const gchar **column_values,
1685 		       gpointer user_data)
1686 {
1687 	guint64 *pui64 = user_data;
1688 
1689 	g_return_val_if_fail (pui64 != NULL, FALSE);
1690 
1691 	if (ncols == 1) {
1692 		*pui64 = column_values[0] ? g_ascii_strtoull (column_values[0], NULL, 10) : 0;
1693 	} else {
1694 		*pui64 = 0;
1695 	}
1696 
1697 	return TRUE;
1698 }
1699 
1700 static gboolean
e_cache_get_int64_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1701 e_cache_get_int64_cb (ECache *cache,
1702 		      gint ncols,
1703 		      const gchar **column_names,
1704 		      const gchar **column_values,
1705 		      gpointer user_data)
1706 {
1707 	gint64 *pi64 = user_data;
1708 
1709 	g_return_val_if_fail (pi64 != NULL, FALSE);
1710 
1711 	if (ncols == 1) {
1712 		*pi64 = column_values[0] ? g_ascii_strtoll (column_values[0], NULL, 10) : 0;
1713 	} else {
1714 		*pi64 = 0;
1715 	}
1716 
1717 	return TRUE;
1718 }
1719 
1720 /**
1721  * e_cache_get_count:
1722  * @cache: an #ECache
1723  * @deleted_flag: one of #ECacheDeletedFlag enum
1724  * @cancellable: optional #GCancellable object, or %NULL
1725  * @error: return location for a #GError, or %NULL
1726  *
1727  * Returns: Count of objects stored in the @cache.
1728  *
1729  * Since: 3.26
1730  **/
1731 guint
e_cache_get_count(ECache * cache,ECacheDeletedFlag deleted_flag,GCancellable * cancellable,GError ** error)1732 e_cache_get_count (ECache *cache,
1733 		   ECacheDeletedFlag deleted_flag,
1734 		   GCancellable *cancellable,
1735 		   GError **error)
1736 {
1737 	guint64 nobjects = 0;
1738 
1739 	g_return_val_if_fail (E_IS_CACHE (cache), 0);
1740 
1741 	if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
1742 		e_cache_sqlite_exec_printf (cache,
1743 			"SELECT COUNT(*) FROM " E_CACHE_TABLE_OBJECTS,
1744 			e_cache_get_uint64_cb, &nobjects, cancellable, error);
1745 	} else {
1746 		e_cache_sqlite_exec_printf (cache,
1747 			"SELECT COUNT(*) FROM " E_CACHE_TABLE_OBJECTS
1748 			" WHERE " E_CACHE_COLUMN_STATE " != %d",
1749 			e_cache_get_uint64_cb, &nobjects, NULL, NULL,
1750 			E_OFFLINE_STATE_LOCALLY_DELETED);
1751 	}
1752 
1753 	return nobjects;
1754 }
1755 
1756 struct GatherRowsData {
1757 	GSList **out_uids;
1758 	GSList **out_revisions;
1759 	GSList **out_objects;
1760 };
1761 
1762 static gboolean
e_cache_gather_rows_data_cb(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,EOfflineState offline_state,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)1763 e_cache_gather_rows_data_cb (ECache *cache,
1764 			     const gchar *uid,
1765 			     const gchar *revision,
1766 			     const gchar *object,
1767 			     EOfflineState offline_state,
1768 			     gint ncols,
1769 			     const gchar *column_names[],
1770 			     const gchar *column_values[],
1771 			     gpointer user_data)
1772 {
1773 	struct GatherRowsData *gd = user_data;
1774 
1775 	g_return_val_if_fail (gd != NULL, FALSE);
1776 
1777 	if (gd->out_uids)
1778 		*gd->out_uids = g_slist_prepend (*gd->out_uids, g_strdup (uid));
1779 
1780 	if (gd->out_revisions)
1781 		*gd->out_revisions = g_slist_prepend (*gd->out_revisions, g_strdup (revision));
1782 
1783 	if (gd->out_objects)
1784 		*gd->out_objects = g_slist_prepend (*gd->out_objects, g_strdup (object));
1785 
1786 	return TRUE;
1787 }
1788 
1789 /**
1790  * e_cache_get_uids:
1791  * @cache: an #ECache
1792  * @deleted_flag: one of #ECacheDeletedFlag enum
1793  * @out_uids: (out) (transfer full) (element-type utf8): a pointer to #GSList to store the found uid to
1794  * @out_revisions: (out) (transfer full) (element-type utf8) (nullable): a pointer to #GSList to store
1795  *    the found revisions to, or %NULL
1796  * @cancellable: optional #GCancellable object, or %NULL
1797  * @error: return location for a #GError, or %NULL
1798  *
1799  * Gets a list of unique object identifiers stored in the @cache, optionally
1800  * together with their revisions. The uids are not returned in any particular
1801  * order, but the position between @out_uids and @out_revisions matches
1802  * the same object.
1803  *
1804  * Both @out_uids and @out_revisions contain newly allocated #GSList, which
1805  * should be freed with g_slist_free_full (slist, g_free); when no longer needed.
1806  *
1807  * Returns: Whether succeeded. It doesn't necessarily mean that there was
1808  *    any object stored in the @cache.
1809  *
1810  * Since: 3.26
1811  **/
1812 gboolean
e_cache_get_uids(ECache * cache,ECacheDeletedFlag deleted_flag,GSList ** out_uids,GSList ** out_revisions,GCancellable * cancellable,GError ** error)1813 e_cache_get_uids (ECache *cache,
1814 		  ECacheDeletedFlag deleted_flag,
1815 		  GSList **out_uids,
1816 		  GSList **out_revisions,
1817 		  GCancellable *cancellable,
1818 		  GError **error)
1819 {
1820 	struct GatherRowsData gr;
1821 
1822 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1823 	g_return_val_if_fail (out_uids, FALSE);
1824 
1825 	gr.out_uids = out_uids;
1826 	gr.out_revisions = out_revisions;
1827 	gr.out_objects = NULL;
1828 
1829 	return e_cache_foreach (cache, deleted_flag, NULL,
1830 		e_cache_gather_rows_data_cb, &gr, cancellable, error);
1831 }
1832 
1833 /**
1834  * e_cache_get_objects:
1835  * @cache: an #ECache
1836  * @deleted_flag: one of #ECacheDeletedFlag enum
1837  * @out_objects: (out) (transfer full) (element-type utf8): a pointer to #GSList to store the found objects to
1838  * @out_revisions: (out) (transfer full) (element-type utf8) (nullable): a pointer to #GSList to store
1839  *    the found revisions to, or %NULL
1840  * @cancellable: optional #GCancellable object, or %NULL
1841  * @error: return location for a #GError, or %NULL
1842  *
1843  * Gets a list of objects stored in the @cache, optionally together with
1844  * their revisions. The uids are not returned in any particular order,
1845  * but the position between @out_objects and @out_revisions matches
1846  * the same object.
1847  *
1848  * Both @out_objects and @out_revisions contain newly allocated #GSList, which
1849  * should be freed with g_slist_free_full (slist, g_free); when no longer needed.
1850  *
1851  * Returns: Whether succeeded. It doesn't necessarily mean that there was
1852  *    any object stored in the @cache.
1853  *
1854  * Since: 3.26
1855  **/
1856 gboolean
e_cache_get_objects(ECache * cache,ECacheDeletedFlag deleted_flag,GSList ** out_objects,GSList ** out_revisions,GCancellable * cancellable,GError ** error)1857 e_cache_get_objects (ECache *cache,
1858 		     ECacheDeletedFlag deleted_flag,
1859 		     GSList **out_objects,
1860 		     GSList **out_revisions,
1861 		     GCancellable *cancellable,
1862 		     GError **error)
1863 {
1864 	struct GatherRowsData gr;
1865 
1866 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1867 	g_return_val_if_fail (out_objects, FALSE);
1868 
1869 	gr.out_uids = NULL;
1870 	gr.out_revisions = out_revisions;
1871 	gr.out_objects = out_objects;
1872 
1873 	return e_cache_foreach (cache, deleted_flag, NULL,
1874 		e_cache_gather_rows_data_cb, &gr, cancellable, error);
1875 }
1876 
1877 struct ForeachData {
1878 	gint uid_index;
1879 	gint revision_index;
1880 	gint object_index;
1881 	gint state_index;
1882 	ECacheForeachFunc func;
1883 	gpointer user_data;
1884 };
1885 
1886 static gboolean
e_cache_foreach_cb(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)1887 e_cache_foreach_cb (ECache *cache,
1888 		    gint ncols,
1889 		    const gchar *column_names[],
1890 		    const gchar *column_values[],
1891 		    gpointer user_data)
1892 {
1893 	struct ForeachData *fe = user_data;
1894 	EOfflineState offline_state;
1895 
1896 	g_return_val_if_fail (fe != NULL, FALSE);
1897 	g_return_val_if_fail (fe->func != NULL, FALSE);
1898 	g_return_val_if_fail (column_names != NULL, FALSE);
1899 	g_return_val_if_fail (column_values != NULL, FALSE);
1900 
1901 	if (fe->uid_index == -1 ||
1902 	    fe->revision_index == -1 ||
1903 	    fe->object_index == -1 ||
1904 	    fe->state_index == -1) {
1905 		gint ii;
1906 
1907 		for (ii = 0; ii < ncols && (fe->uid_index == -1 ||
1908 		     fe->revision_index == -1 ||
1909 		     fe->object_index == -1 ||
1910 		     fe->state_index == -1); ii++) {
1911 			if (!column_names[ii])
1912 				continue;
1913 
1914 			if (fe->uid_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) == 0) {
1915 				fe->uid_index = ii;
1916 			} else if (fe->revision_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) == 0) {
1917 				fe->revision_index = ii;
1918 			} else if (fe->object_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) == 0) {
1919 				fe->object_index = ii;
1920 			} else if (fe->state_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) == 0) {
1921 				fe->state_index = ii;
1922 			}
1923 		}
1924 	}
1925 
1926 	g_return_val_if_fail (fe->uid_index >= 0 && fe->uid_index < ncols, FALSE);
1927 	g_return_val_if_fail (fe->revision_index >= 0 && fe->revision_index < ncols, FALSE);
1928 	g_return_val_if_fail (fe->object_index >= 0 && fe->object_index < ncols, FALSE);
1929 	g_return_val_if_fail (fe->state_index >= 0 && fe->state_index < ncols, FALSE);
1930 
1931 	if (!column_values[fe->state_index])
1932 		offline_state = E_OFFLINE_STATE_UNKNOWN;
1933 	else
1934 		offline_state = g_ascii_strtoull (column_values[fe->state_index], NULL, 10);
1935 
1936 	return fe->func (cache, column_values[fe->uid_index], column_values[fe->revision_index], column_values[fe->object_index],
1937 		offline_state, ncols, column_names, column_values, fe->user_data);
1938 }
1939 
1940 /**
1941  * e_cache_foreach:
1942  * @cache: an #ECache
1943  * @deleted_flag: one of #ECacheDeletedFlag enum
1944  * @where_clause: (nullable): an optional SQLite WHERE clause part, or %NULL
1945  * @func: (scope call): an #ECacheForeachFunc function to call for each object
1946  * @user_data: user data for the @func
1947  * @cancellable: optional #GCancellable object, or %NULL
1948  * @error: return location for a #GError, or %NULL
1949  *
1950  * Calls @func for each found object, which satisfies the criteria
1951  * for both @deleted_flag and @where_clause.
1952  *
1953  * Note the @func should not call any SQLite commands, because it's invoked
1954  * within a SELECT statement execution.
1955  *
1956  * Returns: Whether succeeded.
1957  *
1958  * Since: 3.26
1959  **/
1960 gboolean
e_cache_foreach(ECache * cache,ECacheDeletedFlag deleted_flag,const gchar * where_clause,ECacheForeachFunc func,gpointer user_data,GCancellable * cancellable,GError ** error)1961 e_cache_foreach (ECache *cache,
1962 		 ECacheDeletedFlag deleted_flag,
1963 		 const gchar *where_clause,
1964 		 ECacheForeachFunc func,
1965 		 gpointer user_data,
1966 		 GCancellable *cancellable,
1967 		 GError **error)
1968 {
1969 	struct ForeachData fe;
1970 	GString *stmt;
1971 	gboolean success;
1972 
1973 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1974 	g_return_val_if_fail (func, FALSE);
1975 
1976 	stmt = g_string_new ("SELECT * FROM " E_CACHE_TABLE_OBJECTS);
1977 
1978 	if (where_clause) {
1979 		g_string_append (stmt, " WHERE ");
1980 
1981 		if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
1982 			g_string_append (stmt, where_clause);
1983 		} else {
1984 			g_string_append_printf (stmt, E_CACHE_COLUMN_STATE "!=%d AND (%s)",
1985 				E_OFFLINE_STATE_LOCALLY_DELETED, where_clause);
1986 		}
1987 	} else if (deleted_flag != E_CACHE_INCLUDE_DELETED) {
1988 		g_string_append_printf (stmt, " WHERE " E_CACHE_COLUMN_STATE "!=%d", E_OFFLINE_STATE_LOCALLY_DELETED);
1989 	}
1990 
1991 	fe.func = func;
1992 	fe.user_data = user_data;
1993 	fe.uid_index = -1;
1994 	fe.revision_index = -1;
1995 	fe.object_index = -1;
1996 	fe.state_index = -1;
1997 
1998 	success = e_cache_sqlite_exec_internal (cache, stmt->str, e_cache_foreach_cb, &fe, cancellable, error);
1999 
2000 	g_string_free (stmt, TRUE);
2001 
2002 	return success;
2003 }
2004 
2005 struct ForeachUpdateRowData {
2006 	gchar *uid;
2007 	gchar *revision;
2008 	gchar *object;
2009 	EOfflineState offline_state;
2010 	gint ncols;
2011 	GPtrArray *column_values;
2012 };
2013 
2014 static void
foreach_update_row_data_free(gpointer ptr)2015 foreach_update_row_data_free (gpointer ptr)
2016 {
2017 	struct ForeachUpdateRowData *fr = ptr;
2018 
2019 	if (fr) {
2020 		g_free (fr->uid);
2021 		g_free (fr->revision);
2022 		g_free (fr->object);
2023 		g_ptr_array_free (fr->column_values, TRUE);
2024 		g_slice_free (struct ForeachUpdateRowData, fr);
2025 	}
2026 }
2027 
2028 struct ForeachUpdateData {
2029 	gint uid_index;
2030 	gint revision_index;
2031 	gint object_index;
2032 	gint state_index;
2033 	GSList *rows; /* struct ForeachUpdateRowData * */
2034 	GPtrArray *column_names;
2035 };
2036 
2037 static gboolean
e_cache_foreach_update_cb(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)2038 e_cache_foreach_update_cb (ECache *cache,
2039 			   gint ncols,
2040 			   const gchar *column_names[],
2041 			   const gchar *column_values[],
2042 			   gpointer user_data)
2043 {
2044 	struct ForeachUpdateData *fu = user_data;
2045 	struct ForeachUpdateRowData *rd;
2046 	EOfflineState offline_state;
2047 	GPtrArray *cnames, *cvalues;
2048 	gint ii;
2049 
2050 	g_return_val_if_fail (fu != NULL, FALSE);
2051 	g_return_val_if_fail (column_names != NULL, FALSE);
2052 	g_return_val_if_fail (column_values != NULL, FALSE);
2053 
2054 	if (fu->uid_index == -1 ||
2055 	    fu->revision_index == -1 ||
2056 	    fu->object_index == -1 ||
2057 	    fu->state_index == -1) {
2058 		gint ii;
2059 
2060 		for (ii = 0; ii < ncols && (fu->uid_index == -1 ||
2061 		     fu->revision_index == -1 ||
2062 		     fu->object_index == -1 ||
2063 		     fu->state_index == -1); ii++) {
2064 			if (!column_names[ii])
2065 				continue;
2066 
2067 			if (fu->uid_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) == 0) {
2068 				fu->uid_index = ii;
2069 			} else if (fu->revision_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) == 0) {
2070 				fu->revision_index = ii;
2071 			} else if (fu->object_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) == 0) {
2072 				fu->object_index = ii;
2073 			} else if (fu->state_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) == 0) {
2074 				fu->state_index = ii;
2075 			}
2076 		}
2077 	}
2078 
2079 	g_return_val_if_fail (fu->uid_index >= 0 && fu->uid_index < ncols, FALSE);
2080 	g_return_val_if_fail (fu->revision_index >= 0 && fu->revision_index < ncols, FALSE);
2081 	g_return_val_if_fail (fu->object_index >= 0 && fu->object_index < ncols, FALSE);
2082 	g_return_val_if_fail (fu->state_index >= 0 && fu->state_index < ncols, FALSE);
2083 
2084 	if (!column_values[fu->state_index])
2085 		offline_state = E_OFFLINE_STATE_UNKNOWN;
2086 	else
2087 		offline_state = g_ascii_strtoull (column_values[fu->state_index], NULL, 10);
2088 
2089 	cnames = fu->column_names ? NULL : g_ptr_array_new_full (ncols, g_free);
2090 	cvalues = g_ptr_array_new_full (ncols, g_free);
2091 
2092 	for (ii = 0; ii < ncols; ii++) {
2093 		if (fu->uid_index == ii ||
2094 		    fu->revision_index == ii ||
2095 		    fu->object_index == ii ||
2096 		    fu->state_index == ii) {
2097 			continue;
2098 		}
2099 
2100 		if (cnames)
2101 			g_ptr_array_add (cnames, g_strdup (column_names[ii]));
2102 
2103 		g_ptr_array_add (cvalues, g_strdup (column_values[ii]));
2104 	}
2105 
2106 	rd = g_slice_new0 (struct ForeachUpdateRowData);
2107 	rd->uid = g_strdup (column_values[fu->uid_index]);
2108 	rd->revision = g_strdup (column_values[fu->revision_index]);
2109 	rd->object = g_strdup (column_values[fu->object_index]);
2110 	rd->offline_state = offline_state;
2111 	rd->ncols = cvalues->len;
2112 	rd->column_values = cvalues;
2113 
2114 	if (cnames)
2115 		fu->column_names = cnames;
2116 
2117 	fu->rows = g_slist_prepend (fu->rows, rd);
2118 
2119 	g_return_val_if_fail (fu->column_names && (gint) fu->column_names->len == rd->ncols, FALSE);
2120 
2121 	return TRUE;
2122 }
2123 
2124 /**
2125  * e_cache_foreach_update:
2126  * @cache: an #ECache
2127  * @deleted_flag: one of #ECacheDeletedFlag enum
2128  * @where_clause: (nullable): an optional SQLite WHERE clause part, or %NULL
2129  * @func: (scope call): an #ECacheUpdateFunc function to call for each object
2130  * @user_data: user data for the @func
2131  * @cancellable: optional #GCancellable object, or %NULL
2132  * @error: return location for a #GError, or %NULL
2133  *
2134  * Calls @func for each found object, which satisfies the criteria for both
2135  * @deleted_flag and @where_clause, letting the caller update values where
2136  * necessary. The return value of @func is used to determine whether the call
2137  * was successful, not whether there are any changes to be saved. If anything
2138  * fails during the call then the all changes are reverted.
2139  *
2140  * When there are requested any changes by the @func, this function also
2141  * calls e_cache_copy_missing_to_column_values() to ensure no descendant
2142  * column data is lost.
2143  *
2144  * Returns: Whether succeeded.
2145  *
2146  * Since: 3.26
2147  **/
2148 gboolean
e_cache_foreach_update(ECache * cache,ECacheDeletedFlag deleted_flag,const gchar * where_clause,ECacheUpdateFunc func,gpointer user_data,GCancellable * cancellable,GError ** error)2149 e_cache_foreach_update (ECache *cache,
2150 			ECacheDeletedFlag deleted_flag,
2151 			const gchar *where_clause,
2152 			ECacheUpdateFunc func,
2153 			gpointer user_data,
2154 			GCancellable *cancellable,
2155 			GError **error)
2156 {
2157 	GString *stmt_begin;
2158 	gchar *uid = NULL;
2159 	gint n_results;
2160 	gboolean has_where = TRUE;
2161 	gboolean success;
2162 
2163 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2164 	g_return_val_if_fail (func, FALSE);
2165 
2166 	e_cache_lock (cache, E_CACHE_LOCK_WRITE);
2167 
2168 	stmt_begin = g_string_new ("SELECT * FROM " E_CACHE_TABLE_OBJECTS);
2169 
2170 	if (where_clause) {
2171 		g_string_append (stmt_begin, " WHERE ");
2172 
2173 		if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
2174 			g_string_append (stmt_begin, where_clause);
2175 		} else {
2176 			g_string_append_printf (stmt_begin, E_CACHE_COLUMN_STATE "!=%d AND (%s)",
2177 				E_OFFLINE_STATE_LOCALLY_DELETED, where_clause);
2178 		}
2179 	} else if (deleted_flag != E_CACHE_INCLUDE_DELETED) {
2180 		g_string_append_printf (stmt_begin, " WHERE " E_CACHE_COLUMN_STATE "!=%d", E_OFFLINE_STATE_LOCALLY_DELETED);
2181 	} else {
2182 		has_where = FALSE;
2183 	}
2184 
2185 	do {
2186 		GString *stmt;
2187 		GSList *link;
2188 		struct ForeachUpdateData fu;
2189 
2190 		fu.uid_index = -1;
2191 		fu.revision_index = -1;
2192 		fu.object_index = -1;
2193 		fu.state_index = -1;
2194 		fu.rows = NULL;
2195 		fu.column_names = NULL;
2196 
2197 		stmt = g_string_new (stmt_begin->str);
2198 
2199 		if (uid) {
2200 			if (has_where)
2201 				g_string_append (stmt, " AND ");
2202 			else
2203 				g_string_append (stmt, " WHERE ");
2204 
2205 			e_cache_sqlite_stmt_append_printf (stmt, E_CACHE_COLUMN_UID ">%Q", uid);
2206 		}
2207 
2208 		g_string_append_printf (stmt, " ORDER BY " E_CACHE_COLUMN_UID " ASC LIMIT %d", E_CACHE_UPDATE_BATCH_SIZE);
2209 
2210 		success = e_cache_sqlite_exec_internal (cache, stmt->str, e_cache_foreach_update_cb, &fu, cancellable, error);
2211 
2212 		g_string_free (stmt, TRUE);
2213 
2214 		if (success) {
2215 			n_results = 0;
2216 			fu.rows = g_slist_reverse (fu.rows);
2217 
2218 			for (link = fu.rows; success && link; link = g_slist_next (link), n_results++) {
2219 				struct ForeachUpdateRowData *fr = link->data;
2220 
2221 				success = fr && fr->column_values && fu.column_names;
2222 				if (success) {
2223 					gchar *new_revision = NULL;
2224 					gchar *new_object = NULL;
2225 					EOfflineState new_offline_state = fr->offline_state;
2226 					ECacheColumnValues *new_other_columns = NULL;
2227 
2228 					success = func (cache, fr->uid, fr->revision, fr->object, fr->offline_state,
2229 						fr->ncols, (const gchar **) fu.column_names->pdata,
2230 						(const gchar **) fr->column_values->pdata,
2231 						&new_revision, &new_object, &new_offline_state, &new_other_columns,
2232 						user_data);
2233 
2234 					if (success && (
2235 					    (new_revision && g_strcmp0 (new_revision, fr->revision) != 0) ||
2236 					    (new_object && g_strcmp0 (new_object, fr->object) != 0) ||
2237 					    (new_offline_state != fr->offline_state) ||
2238 					    (new_other_columns && e_cache_column_values_get_size (new_other_columns) > 0))) {
2239 						if (!new_other_columns)
2240 							new_other_columns = e_cache_column_values_new ();
2241 
2242 						e_cache_copy_missing_to_column_values (cache, fr->ncols,
2243 							(const gchar **) fu.column_names->pdata,
2244 							(const gchar **) fr->column_values->pdata,
2245 							new_other_columns);
2246 
2247 						success = e_cache_put_locked (cache,
2248 							fr->uid,
2249 							new_revision ? new_revision : fr->revision,
2250 							new_object ? new_object : fr->object,
2251 							new_other_columns,
2252 							new_offline_state,
2253 							TRUE, cancellable, error);
2254 					}
2255 
2256 					g_free (new_revision);
2257 					g_free (new_object);
2258 					e_cache_column_values_free (new_other_columns);
2259 
2260 					if (!g_slist_next (link)) {
2261 						g_free (uid);
2262 						uid = g_strdup (fr->uid);
2263 					}
2264 				}
2265 			}
2266 		}
2267 
2268 		g_slist_free_full (fu.rows, foreach_update_row_data_free);
2269 		if (fu.column_names)
2270 			g_ptr_array_free (fu.column_names, TRUE);
2271 	} while (success && n_results == E_CACHE_UPDATE_BATCH_SIZE);
2272 
2273 	g_string_free (stmt_begin, TRUE);
2274 	g_free (uid);
2275 
2276 	e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
2277 
2278 	return success;
2279 }
2280 
2281 /**
2282  * e_cache_copy_missing_to_column_values:
2283  * @cache: an #ECache
2284  * @ncols: count of columns, items in column_names and column_values
2285  * @column_names: (array length=ncols) (element-type utf8): column names
2286  * @column_values: (array length=ncols) (element-type utf8): column values
2287  * @other_columns: (inout): an #ECacheColumnValues to fill
2288  *
2289  * Adds every column value which is not part of the @other_columns to it,
2290  * except of E_CACHE_COLUMN_UID, E_CACHE_COLUMN_REVISION, E_CACHE_COLUMN_OBJECT
2291  * and E_CACHE_COLUMN_STATE columns.
2292  *
2293  * This can be used within the callback of e_cache_foreach_update().
2294  *
2295  * Since: 3.32
2296  **/
2297 void
e_cache_copy_missing_to_column_values(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],ECacheColumnValues * other_columns)2298 e_cache_copy_missing_to_column_values (ECache *cache,
2299 				       gint ncols,
2300 				       const gchar *column_names[],
2301 				       const gchar *column_values[],
2302 				       ECacheColumnValues *other_columns)
2303 {
2304 	gint ii;
2305 
2306 	g_return_if_fail (E_IS_CACHE (cache));
2307 	g_return_if_fail (column_names != NULL);
2308 	g_return_if_fail (column_values != NULL);
2309 	g_return_if_fail (other_columns != NULL);
2310 
2311 	for (ii = 0; ii < ncols; ii++) {
2312 		if (column_names[ii] && column_values[ii] &&
2313 		    !e_cache_column_values_contains (other_columns, column_names[ii]) &&
2314 		    g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) != 0 &&
2315 		    g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) != 0 &&
2316 		    g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) != 0 &&
2317 		    g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) != 0) {
2318 			e_cache_column_values_put (other_columns, column_names[ii], column_values[ii]);
2319 		}
2320 	}
2321 }
2322 
2323 /**
2324  * e_cache_get_offline_state:
2325  * @cache: an #ECache
2326  * @uid: a unique identifier of an object
2327  * @cancellable: optional #GCancellable object, or %NULL
2328  * @error: return location for a #GError, or %NULL
2329  *
2330  * Returns: Current offline state #EOfflineState for the given object.
2331  *    It returns %E_OFFLINE_STATE_UNKNOWN when the object could not be
2332  *    found or other error happened.
2333  *
2334  * Since: 3.26
2335  **/
2336 EOfflineState
e_cache_get_offline_state(ECache * cache,const gchar * uid,GCancellable * cancellable,GError ** error)2337 e_cache_get_offline_state (ECache *cache,
2338 			   const gchar *uid,
2339 			   GCancellable *cancellable,
2340 			   GError **error)
2341 {
2342 	EOfflineState offline_state = E_OFFLINE_STATE_UNKNOWN;
2343 	gint64 value = offline_state;
2344 
2345 	g_return_val_if_fail (E_IS_CACHE (cache), E_OFFLINE_STATE_UNKNOWN);
2346 	g_return_val_if_fail (uid != NULL, E_OFFLINE_STATE_UNKNOWN);
2347 
2348 	if (!e_cache_contains (cache, uid, E_CACHE_INCLUDE_DELETED)) {
2349 		g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object “%s” not found"), uid);
2350 		return offline_state;
2351 	}
2352 
2353 	if (e_cache_sqlite_exec_printf (cache,
2354 		"SELECT " E_CACHE_COLUMN_STATE " FROM " E_CACHE_TABLE_OBJECTS
2355 		" WHERE " E_CACHE_COLUMN_UID " = %Q",
2356 		e_cache_get_int64_cb, &value, cancellable, error,
2357 		uid)) {
2358 		offline_state = value;
2359 	}
2360 
2361 	return offline_state;
2362 }
2363 
2364 /**
2365  * e_cache_set_offline_state:
2366  * @cache: an #ECache
2367  * @uid: a unique identifier of an object
2368  * @state: an #EOfflineState to set
2369  * @cancellable: optional #GCancellable object, or %NULL
2370  * @error: return location for a #GError, or %NULL
2371  *
2372  * Sets an offline @state for the object identified by @uid.
2373  *
2374  * Returns: Whether succeeded.
2375  *
2376  * Since: 3.26
2377  **/
2378 gboolean
e_cache_set_offline_state(ECache * cache,const gchar * uid,EOfflineState state,GCancellable * cancellable,GError ** error)2379 e_cache_set_offline_state (ECache *cache,
2380 			   const gchar *uid,
2381 			   EOfflineState state,
2382 			   GCancellable *cancellable,
2383 			   GError **error)
2384 {
2385 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2386 	g_return_val_if_fail (uid != NULL, FALSE);
2387 
2388 	if (!e_cache_contains (cache, uid, E_CACHE_INCLUDE_DELETED)) {
2389 		g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object “%s” not found"), uid);
2390 		return FALSE;
2391 	}
2392 
2393 	return e_cache_sqlite_exec_printf (cache,
2394 		"UPDATE " E_CACHE_TABLE_OBJECTS " SET " E_CACHE_COLUMN_STATE "=%d"
2395 		" WHERE " E_CACHE_COLUMN_UID " = %Q",
2396 		NULL, NULL, cancellable, error,
2397 		state, uid);
2398 }
2399 
2400 static gboolean
e_cache_get_offline_changes_cb(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,EOfflineState offline_state,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)2401 e_cache_get_offline_changes_cb (ECache *cache,
2402 				const gchar *uid,
2403 				const gchar *revision,
2404 				const gchar *object,
2405 				EOfflineState offline_state,
2406 				gint ncols,
2407 				const gchar *column_names[],
2408 				const gchar *column_values[],
2409 				gpointer user_data)
2410 {
2411 	GSList **pchanges = user_data;
2412 
2413 	g_return_val_if_fail (pchanges != NULL, FALSE);
2414 
2415 	if (offline_state == E_OFFLINE_STATE_LOCALLY_CREATED ||
2416 	    offline_state == E_OFFLINE_STATE_LOCALLY_MODIFIED ||
2417 	    offline_state == E_OFFLINE_STATE_LOCALLY_DELETED) {
2418 		*pchanges = g_slist_prepend (*pchanges, e_cache_offline_change_new (uid, revision, object, offline_state));
2419 	}
2420 
2421 	return TRUE;
2422 }
2423 
2424 /**
2425  * e_cache_get_offline_changes:
2426  * @cache: an #ECache
2427  * @cancellable: optional #GCancellable object, or %NULL
2428  * @error: return location for a #GError, or %NULL
2429  *
2430  * Gathers the list of all offline changes being done so far.
2431  * The returned #GSList contains #ECacheOfflineChange structure.
2432  * Use e_cache_clear_offline_changes() to clear all offline
2433  * changes at once.
2434  *
2435  * Returns: (transfer full) (element-type ECacheOfflineChange): A newly allocated list of all
2436  *    offline changes. Free it with g_slist_free_full (slist, e_cache_offline_change_free);
2437  *    when no longer needed.
2438  *
2439  * Since: 3.26
2440  **/
2441 GSList *
e_cache_get_offline_changes(ECache * cache,GCancellable * cancellable,GError ** error)2442 e_cache_get_offline_changes (ECache *cache,
2443 			     GCancellable *cancellable,
2444 			     GError **error)
2445 {
2446 	GSList *changes = NULL;
2447 	gchar *stmt;
2448 
2449 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
2450 
2451 	stmt = e_cache_sqlite_stmt_printf (E_CACHE_COLUMN_STATE "!=%d", E_OFFLINE_STATE_SYNCED);
2452 
2453 	if (!e_cache_foreach (cache, E_CACHE_INCLUDE_DELETED, stmt, e_cache_get_offline_changes_cb, &changes, cancellable, error)) {
2454 		g_slist_free_full (changes, e_cache_offline_change_free);
2455 		changes = NULL;
2456 	}
2457 
2458 	e_cache_sqlite_stmt_free (stmt);
2459 
2460 	return changes;
2461 }
2462 
2463 /**
2464  * e_cache_clear_offline_changes:
2465  * @cache: an #ECache
2466  * @cancellable: optional #GCancellable object, or %NULL
2467  * @error: return location for a #GError, or %NULL
2468  *
2469  * Marks all objects as being fully synchronized with the server and
2470  * removes those which are marked as locally deleted.
2471  *
2472  * Returns: Whether succeeded.
2473  *
2474  * Since: 3.26
2475  **/
2476 gboolean
e_cache_clear_offline_changes(ECache * cache,GCancellable * cancellable,GError ** error)2477 e_cache_clear_offline_changes (ECache *cache,
2478 			       GCancellable *cancellable,
2479 			       GError **error)
2480 {
2481 	ECacheClass *klass;
2482 	gboolean success;
2483 
2484 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2485 
2486 	klass = E_CACHE_GET_CLASS (cache);
2487 	g_return_val_if_fail (klass != NULL, FALSE);
2488 	g_return_val_if_fail (klass->clear_offline_changes_locked != NULL, FALSE);
2489 
2490 	e_cache_lock (cache, E_CACHE_LOCK_WRITE);
2491 
2492 	success = klass->clear_offline_changes_locked (cache, cancellable, error);
2493 
2494 	e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
2495 
2496 	return success;
2497 }
2498 
2499 /**
2500  * e_cache_set_key:
2501  * @cache: an #ECache
2502  * @key: a key name
2503  * @value: (nullable): a value to set, or %NULL to delete the key
2504  * @error: return location for a #GError, or %NULL
2505  *
2506  * Sets a @value of the user @key, or deletes it, if the @value is %NULL.
2507  *
2508  * Returns: Whether succeeded.
2509  *
2510  * Since: 3.26
2511  **/
2512 gboolean
e_cache_set_key(ECache * cache,const gchar * key,const gchar * value,GError ** error)2513 e_cache_set_key (ECache *cache,
2514 		 const gchar *key,
2515 		 const gchar *value,
2516 		 GError **error)
2517 {
2518 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2519 	g_return_val_if_fail (key != NULL, FALSE);
2520 
2521 	return e_cache_set_key_internal (cache, TRUE, key, value, error);
2522 }
2523 
2524 /**
2525  * e_cache_dup_key:
2526  * @cache: an #ECache
2527  * @key: a key name
2528  * @error: return location for a #GError, or %NULL
2529  *
2530  * Returns: (transfer full): a value of the @key. Free the returned string
2531  *    with g_free(), when no longer needed.
2532  *
2533  * Since: 3.26
2534  **/
2535 gchar *
e_cache_dup_key(ECache * cache,const gchar * key,GError ** error)2536 e_cache_dup_key (ECache *cache,
2537 		 const gchar *key,
2538 		 GError **error)
2539 {
2540 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
2541 	g_return_val_if_fail (key != NULL, NULL);
2542 
2543 	return e_cache_dup_key_internal (cache, TRUE, key, error);
2544 }
2545 
2546 /**
2547  * e_cache_set_key_int:
2548  * @cache: an #ECache
2549  * @key: a key name
2550  * @value: an integer value to set
2551  * @error: return location for a #GError, or %NULL
2552  *
2553  * Sets an integer @value for the user @key.
2554  *
2555  * Returns: Whether succeeded.
2556  *
2557  * Since: 3.26
2558  **/
2559 gboolean
e_cache_set_key_int(ECache * cache,const gchar * key,gint value,GError ** error)2560 e_cache_set_key_int (ECache *cache,
2561 		     const gchar *key,
2562 		     gint value,
2563 		     GError **error)
2564 {
2565 	gchar *str_value;
2566 	gboolean success;
2567 
2568 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2569 	g_return_val_if_fail (key != NULL, FALSE);
2570 
2571 	str_value = g_strdup_printf ("%d", value);
2572 	success = e_cache_set_key (cache, key, str_value, error);
2573 	g_free (str_value);
2574 
2575 	return success;
2576 }
2577 
2578 /**
2579  * e_cache_get_key_int:
2580  * @cache: an #ECache
2581  * @key: a key name
2582  * @error: return location for a #GError, or %NULL
2583  *
2584  * Reads the user @key value as an integer.
2585  *
2586  * Returns: The user @key value or -1 on error.
2587  *
2588  * Since: 3.26
2589  **/
2590 gint
e_cache_get_key_int(ECache * cache,const gchar * key,GError ** error)2591 e_cache_get_key_int (ECache *cache,
2592 		     const gchar *key,
2593 		     GError **error)
2594 {
2595 	gchar *str_value;
2596 	gint value;
2597 
2598 	g_return_val_if_fail (E_IS_CACHE (cache), -1);
2599 
2600 	str_value = e_cache_dup_key (cache, key, error);
2601 	if (!str_value)
2602 		return -1;
2603 
2604 	value = g_ascii_strtoll (str_value, NULL, 10);
2605 	g_free (str_value);
2606 
2607 	return value;
2608 }
2609 
2610 /**
2611  * e_cache_lock:
2612  * @cache: an #ECache
2613  * @lock_type: an #ECacheLockType
2614  *
2615  * Locks the @cache thus other threads cannot use it.
2616  * This can be called recursively within one thread.
2617  * Each call should have its pair e_cache_unlock().
2618  *
2619  * Since: 3.26
2620  **/
2621 void
e_cache_lock(ECache * cache,ECacheLockType lock_type)2622 e_cache_lock (ECache *cache,
2623 	      ECacheLockType lock_type)
2624 {
2625 	g_return_if_fail (E_IS_CACHE (cache));
2626 
2627 	g_rec_mutex_lock (&cache->priv->lock);
2628 
2629 	cache->priv->in_transaction++;
2630 	g_return_if_fail (cache->priv->in_transaction > 0);
2631 
2632 	if (cache->priv->in_transaction == 1) {
2633 		/* It's important to make the distinction between a
2634 		 * transaction which will read or one which will write.
2635 		 *
2636 		 * While it's not well documented, when receiving the SQLITE_BUSY
2637 		 * error status, one can only safely retry at the beginning of
2638 		 * the transaction.
2639 		 *
2640 		 * If a transaction is 'upgraded' to require a writer lock
2641 		 * half way through the transaction and SQLITE_BUSY is returned,
2642 		 * the whole transaction would need to be retried from the beginning.
2643 		 */
2644 		cache->priv->lock_type = lock_type;
2645 
2646 		switch (lock_type) {
2647 		case E_CACHE_LOCK_READ:
2648 			e_cache_sqlite_exec_internal (cache, "BEGIN", NULL, NULL, NULL, NULL);
2649 			break;
2650 		case E_CACHE_LOCK_WRITE:
2651 			e_cache_sqlite_exec_internal (cache, "BEGIN IMMEDIATE", NULL, NULL, NULL, NULL);
2652 			break;
2653 		}
2654 	} else {
2655 		/* Warn about cases where a read transaction might be upgraded */
2656 		if (lock_type == E_CACHE_LOCK_WRITE && cache->priv->lock_type == E_CACHE_LOCK_READ)
2657 			g_warning (
2658 				"A nested transaction wants to write, "
2659 				"but the outermost transaction was started "
2660 				"without a writer lock.");
2661 	}
2662 }
2663 
2664 /**
2665  * e_cache_unlock:
2666  * @cache: an #ECache
2667  * @action: an #ECacheUnlockAction
2668  *
2669  * Unlocks the cache which was previouly locked with e_cache_lock().
2670  * The cache locked with #E_CACHE_LOCK_WRITE should use either
2671  * @action #E_CACHE_UNLOCK_COMMIT or #E_CACHE_UNLOCK_ROLLBACK,
2672  * while the #E_CACHE_LOCK_READ should use #E_CACHE_UNLOCK_NONE @action.
2673  *
2674  * Since: 3.26
2675  **/
2676 void
e_cache_unlock(ECache * cache,ECacheUnlockAction action)2677 e_cache_unlock (ECache *cache,
2678 		ECacheUnlockAction action)
2679 {
2680 	g_return_if_fail (E_IS_CACHE (cache));
2681 	g_return_if_fail (cache->priv->in_transaction > 0);
2682 
2683 	cache->priv->in_transaction--;
2684 
2685 	if (cache->priv->in_transaction == 0) {
2686 		switch (action) {
2687 		case E_CACHE_UNLOCK_NONE:
2688 		case E_CACHE_UNLOCK_COMMIT:
2689 			e_cache_sqlite_exec_internal (cache, "COMMIT", NULL, NULL, NULL, NULL);
2690 			break;
2691 		case E_CACHE_UNLOCK_ROLLBACK:
2692 			e_cache_sqlite_exec_internal (cache, "ROLLBACK", NULL, NULL, NULL, NULL);
2693 			break;
2694 		}
2695 	}
2696 
2697 	g_rec_mutex_unlock (&cache->priv->lock);
2698 }
2699 
2700 /**
2701  * e_cache_get_sqlitedb:
2702  * @cache: an #ECache
2703  *
2704  * Returns: (transfer none): An SQLite3 database pointer. It is owned by the @cache.
2705  *
2706  * Since: 3.26
2707  **/
2708 gpointer
e_cache_get_sqlitedb(ECache * cache)2709 e_cache_get_sqlitedb (ECache *cache)
2710 {
2711 	g_return_val_if_fail (E_IS_CACHE (cache), NULL);
2712 
2713 	return cache->priv->db;
2714 }
2715 
2716 /**
2717  * e_cache_sqlite_exec:
2718  * @cache: an #ECache
2719  * @sql_stmt: an SQLite statement to execute
2720  * @cancellable: optional #GCancellable object, or %NULL
2721  * @error: return location for a #GError, or %NULL
2722  *
2723  * Executes an SQLite statement. Use e_cache_sqlite_select() for
2724  * SELECT statements.
2725  *
2726  * Returns: Whether succeeded.
2727  *
2728  * Since: 3.26
2729  **/
2730 gboolean
e_cache_sqlite_exec(ECache * cache,const gchar * sql_stmt,GCancellable * cancellable,GError ** error)2731 e_cache_sqlite_exec (ECache *cache,
2732 		     const gchar *sql_stmt,
2733 		     GCancellable *cancellable,
2734 		     GError **error)
2735 {
2736 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2737 
2738 	return e_cache_sqlite_exec_internal (cache, sql_stmt, NULL, NULL, cancellable, error);
2739 }
2740 
2741 /**
2742  * e_cache_sqlite_select:
2743  * @cache: an #ECache
2744  * @sql_stmt: an SQLite SELECT statement to execute
2745  * @func: (scope call): an #ECacheSelectFunc function to call for each row
2746  * @user_data: user data for @func
2747  * @cancellable: optional #GCancellable object, or %NULL
2748  * @error: return location for a #GError, or %NULL
2749  *
2750  * Executes a SELECT statement @sql_stmt and calls @func for each row of the result.
2751  * Use e_cache_sqlite_exec() for statements which do not return row sets.
2752  *
2753  * Returns: Whether succeeded.
2754  *
2755  * Since: 3.26
2756  **/
2757 gboolean
e_cache_sqlite_select(ECache * cache,const gchar * sql_stmt,ECacheSelectFunc func,gpointer user_data,GCancellable * cancellable,GError ** error)2758 e_cache_sqlite_select (ECache *cache,
2759 		       const gchar *sql_stmt,
2760 		       ECacheSelectFunc func,
2761 		       gpointer user_data,
2762 		       GCancellable *cancellable,
2763 		       GError **error)
2764 {
2765 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2766 	g_return_val_if_fail (sql_stmt, FALSE);
2767 	g_return_val_if_fail (func, FALSE);
2768 
2769 	return e_cache_sqlite_exec_internal (cache, sql_stmt, func, user_data, cancellable, error);
2770 }
2771 
2772 /**
2773  * e_cache_sqlite_stmt_append_printf:
2774  * @stmt: a #GString statement to append to
2775  * @format: a printf-like format
2776  * @...: arguments for the @format
2777  *
2778  * Appends an SQLite statement fragment based on the @format and
2779  * its arguments to the @stmt.
2780  * The @format can contain any values recognized by sqlite3_mprintf().
2781  *
2782  * Since: 3.26
2783  **/
2784 void
e_cache_sqlite_stmt_append_printf(GString * stmt,const gchar * format,...)2785 e_cache_sqlite_stmt_append_printf (GString *stmt,
2786 				   const gchar *format,
2787 				   ...)
2788 {
2789 	va_list args;
2790 	gchar *tmp_stmt;
2791 
2792 	g_return_if_fail (stmt != NULL);
2793 	g_return_if_fail (format != NULL);
2794 
2795 	va_start (args, format);
2796 	tmp_stmt = sqlite3_vmprintf (format, args);
2797 	va_end (args);
2798 
2799 	g_string_append (stmt, tmp_stmt);
2800 
2801 	sqlite3_free (tmp_stmt);
2802 }
2803 
2804 /**
2805  * e_cache_sqlite_stmt_printf:
2806  * @format: a printf-like format
2807  * @...: arguments for the @format
2808  *
2809  * Creates an SQLite statement based on the @format and its arguments.
2810  * The @format can contain any values recognized by sqlite3_mprintf().
2811  *
2812  * Returns: (transfer full): A new SQLite statement. Free the returned
2813  *    string with e_cache_sqlite_stmt_free() when no longer needed.
2814  *
2815  * Since: 3.26
2816  **/
2817 gchar *
e_cache_sqlite_stmt_printf(const gchar * format,...)2818 e_cache_sqlite_stmt_printf (const gchar *format,
2819 			    ...)
2820 {
2821 	va_list args;
2822 	gchar *stmt;
2823 
2824 	g_return_val_if_fail (format != NULL, NULL);
2825 
2826 	va_start (args, format);
2827 	stmt = sqlite3_vmprintf (format, args);
2828 	va_end (args);
2829 
2830 	return stmt;
2831 }
2832 
2833 /**
2834  * e_cache_sqlite_stmt_free:
2835  * @stmt: a statement to free
2836  *
2837  * Frees a statement previously constructed with e_cache_sqlite_stmt_printf().
2838  *
2839  * Since: 3.26
2840  **/
2841 void
e_cache_sqlite_stmt_free(gchar * stmt)2842 e_cache_sqlite_stmt_free (gchar *stmt)
2843 {
2844 	if (stmt)
2845 		sqlite3_free (stmt);
2846 }
2847 
2848 /**
2849  * e_cache_sqlite_maybe_vacuum:
2850  * @cache: an #ECache
2851  * @cancellable: optional #GCancellable object, or %NULL
2852  * @error: return location for a #GError, or %NULL
2853  *
2854  * Runs vacuum (compacts the database file), if needed.
2855  *
2856  * Returns: Whether succeeded. It doesn't mean that the vacuum had been run,
2857  *    only that no error happened during the call.
2858  *
2859  * Since: 3.26
2860  **/
2861 gboolean
e_cache_sqlite_maybe_vacuum(ECache * cache,GCancellable * cancellable,GError ** error)2862 e_cache_sqlite_maybe_vacuum (ECache *cache,
2863 			     GCancellable *cancellable,
2864 			     GError **error)
2865 {
2866 	guint64 page_count = 0, page_size = 0, freelist_count = 0;
2867 	gboolean success = FALSE;
2868 	GError *local_error = NULL;
2869 
2870 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2871 
2872 	g_rec_mutex_lock (&cache->priv->lock);
2873 
2874 	if (e_cache_sqlite_exec_internal (cache, "PRAGMA page_count;", e_cache_get_uint64_cb, &page_count, cancellable, &local_error) &&
2875 	    e_cache_sqlite_exec_internal (cache, "PRAGMA page_size;", e_cache_get_uint64_cb, &page_size, cancellable, &local_error) &&
2876 	    e_cache_sqlite_exec_internal (cache, "PRAGMA freelist_count;", e_cache_get_uint64_cb, &freelist_count, cancellable, &local_error)) {
2877 		/* Vacuum, if there's more than 5% of the free pages, or when free pages use more than 10MB */
2878 		success = !page_count || !freelist_count ||
2879 			(freelist_count * page_size < 1024 * 1024 * 10 && freelist_count * 1000 / page_count <= 50) ||
2880 			e_cache_sqlite_exec_internal (cache, "vacuum;", NULL, NULL, cancellable, &local_error);
2881 	}
2882 
2883 	g_rec_mutex_unlock (&cache->priv->lock);
2884 
2885 	if (local_error) {
2886 		g_propagate_error (error, local_error);
2887 		success = FALSE;
2888 	}
2889 
2890 	return success;
2891 }
2892 
2893 static gboolean
e_cache_put_locked_default(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,EOfflineState offline_state,gboolean is_replace,GCancellable * cancellable,GError ** error)2894 e_cache_put_locked_default (ECache *cache,
2895 			    const gchar *uid,
2896 			    const gchar *revision,
2897 			    const gchar *object,
2898 			    ECacheColumnValues *other_columns,
2899 			    EOfflineState offline_state,
2900 			    gboolean is_replace,
2901 			    GCancellable *cancellable,
2902 			    GError **error)
2903 {
2904 	GString *statement, *other_names = NULL, *other_values = NULL;
2905 	gboolean success;
2906 
2907 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2908 	g_return_val_if_fail (uid != NULL, FALSE);
2909 	g_return_val_if_fail (object != NULL, FALSE);
2910 
2911 	statement = g_string_sized_new (255);
2912 
2913 	e_cache_sqlite_stmt_append_printf (statement, "INSERT OR REPLACE INTO %Q ("
2914 		E_CACHE_COLUMN_UID ","
2915 		E_CACHE_COLUMN_REVISION ","
2916 		E_CACHE_COLUMN_OBJECT ","
2917 		E_CACHE_COLUMN_STATE,
2918 		E_CACHE_TABLE_OBJECTS);
2919 
2920 	if (other_columns) {
2921 		GHashTableIter iter;
2922 		gpointer key, value;
2923 
2924 		e_cache_column_values_init_iter (other_columns, &iter);
2925 		while (g_hash_table_iter_next (&iter, &key, &value)) {
2926 			if (!other_names)
2927 				other_names = g_string_new ("");
2928 			g_string_append_c (other_names, ',');
2929 
2930 			e_cache_sqlite_stmt_append_printf (other_names, "%Q", key);
2931 
2932 			if (!other_values)
2933 				other_values = g_string_new ("");
2934 
2935 			g_string_append_c (other_values, ',');
2936 			if (value) {
2937 				e_cache_sqlite_stmt_append_printf (other_values, "%Q", value);
2938 			} else {
2939 				g_string_append (other_values, "NULL");
2940 			}
2941 		}
2942 	}
2943 
2944 	if (other_names)
2945 		g_string_append (statement, other_names->str);
2946 
2947 	g_string_append (statement, ") VALUES (");
2948 
2949 	e_cache_sqlite_stmt_append_printf (statement, "%Q,%Q,%Q,%d", uid, revision ? revision : "", object, offline_state);
2950 
2951 	if (other_values)
2952 		g_string_append (statement, other_values->str);
2953 
2954 	g_string_append_c (statement, ')');
2955 
2956 	success = e_cache_sqlite_exec_internal (cache, statement->str, NULL, NULL, cancellable, error);
2957 
2958 	if (other_names)
2959 		g_string_free (other_names, TRUE);
2960 	if (other_values)
2961 		g_string_free (other_values, TRUE);
2962 	g_string_free (statement, TRUE);
2963 
2964 	return success;
2965 }
2966 
2967 static gboolean
e_cache_remove_locked_default(ECache * cache,const gchar * uid,GCancellable * cancellable,GError ** error)2968 e_cache_remove_locked_default (ECache *cache,
2969 			       const gchar *uid,
2970 			       GCancellable *cancellable,
2971 			       GError **error)
2972 {
2973 	gboolean success = TRUE;
2974 
2975 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2976 	g_return_val_if_fail (uid != NULL, FALSE);
2977 
2978 	g_signal_emit (cache,
2979 		       signals[BEFORE_REMOVE],
2980 		       0,
2981 		       uid, cancellable, error,
2982 		       &success);
2983 
2984 	success = success && e_cache_sqlite_exec_printf (cache,
2985 		"DELETE FROM " E_CACHE_TABLE_OBJECTS " WHERE " E_CACHE_COLUMN_UID " = %Q",
2986 		NULL, NULL, cancellable, error,
2987 		uid);
2988 
2989 	return success;
2990 }
2991 
2992 static gboolean
e_cache_remove_all_locked_default(ECache * cache,const GSList * uids,GCancellable * cancellable,GError ** error)2993 e_cache_remove_all_locked_default (ECache *cache,
2994 				   const GSList *uids,
2995 				   GCancellable *cancellable,
2996 				   GError **error)
2997 {
2998 	const GSList *link;
2999 	gboolean success = TRUE;
3000 
3001 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
3002 
3003 	for (link = uids; link && success; link = g_slist_next (link)) {
3004 		const gchar *uid = link->data;
3005 
3006 		g_signal_emit (cache,
3007 			       signals[BEFORE_REMOVE],
3008 			       0,
3009 			       uid, cancellable, error,
3010 			       &success);
3011 	}
3012 
3013 	if (success) {
3014 		success = e_cache_sqlite_exec_printf (cache,
3015 			"DELETE FROM " E_CACHE_TABLE_OBJECTS,
3016 			NULL, NULL, cancellable, error);
3017 	}
3018 
3019 	return success;
3020 }
3021 
3022 static gboolean
e_cache_clear_offline_changes_locked_default(ECache * cache,GCancellable * cancellable,GError ** error)3023 e_cache_clear_offline_changes_locked_default (ECache *cache,
3024 					      GCancellable *cancellable,
3025 					      GError **error)
3026 {
3027 	gboolean success;
3028 
3029 	g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
3030 
3031 	success = e_cache_sqlite_exec_printf (cache,
3032 		"DELETE FROM " E_CACHE_TABLE_OBJECTS " WHERE " E_CACHE_COLUMN_STATE "=%d",
3033 		NULL, NULL, cancellable, error,
3034 		E_OFFLINE_STATE_LOCALLY_DELETED);
3035 
3036 	success = success && e_cache_sqlite_exec_printf (cache,
3037 		"UPDATE " E_CACHE_TABLE_OBJECTS " SET " E_CACHE_COLUMN_STATE "=%d"
3038 		" WHERE " E_CACHE_COLUMN_STATE "!=%d",
3039 		NULL, NULL, cancellable, error,
3040 		E_OFFLINE_STATE_SYNCED, E_OFFLINE_STATE_SYNCED);
3041 
3042 	return success;
3043 }
3044 
3045 static gboolean
e_cache_signals_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer data)3046 e_cache_signals_accumulator (GSignalInvocationHint *ihint,
3047 			     GValue *return_accu,
3048 			     const GValue *handler_return,
3049 			     gpointer data)
3050 {
3051 	gboolean handler_result;
3052 
3053 	handler_result = g_value_get_boolean (handler_return);
3054 	g_value_set_boolean (return_accu, handler_result);
3055 
3056 	return handler_result;
3057 }
3058 
3059 static gboolean
e_cache_before_put_default(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,gboolean is_replace,GCancellable * cancellable,GError ** error)3060 e_cache_before_put_default (ECache *cache,
3061 			    const gchar *uid,
3062 			    const gchar *revision,
3063 			    const gchar *object,
3064 			    ECacheColumnValues *other_columns,
3065 			    gboolean is_replace,
3066 			    GCancellable *cancellable,
3067 			    GError **error)
3068 {
3069 	return TRUE;
3070 }
3071 
3072 static gboolean
e_cache_before_remove_default(ECache * cache,const gchar * uid,GCancellable * cancellable,GError ** error)3073 e_cache_before_remove_default (ECache *cache,
3074 			       const gchar *uid,
3075 			       GCancellable *cancellable,
3076 			       GError **error)
3077 {
3078 	return TRUE;
3079 }
3080 
3081 static void
e_cache_finalize(GObject * object)3082 e_cache_finalize (GObject *object)
3083 {
3084 	ECache *cache = E_CACHE (object);
3085 
3086 	g_free (cache->priv->filename);
3087 	cache->priv->filename = NULL;
3088 
3089 	g_clear_pointer (&cache->priv->db, sqlite3_close);
3090 
3091 	g_rec_mutex_clear (&cache->priv->lock);
3092 
3093 	g_warn_if_fail (cache->priv->cancellable == NULL);
3094 	g_clear_object (&cache->priv->cancellable);
3095 
3096 	/* Chain up to parent's method. */
3097 	G_OBJECT_CLASS (e_cache_parent_class)->finalize (object);
3098 }
3099 
3100 static void
e_cache_class_init(ECacheClass * klass)3101 e_cache_class_init (ECacheClass *klass)
3102 {
3103 	GObjectClass *object_class;
3104 
3105 	object_class = G_OBJECT_CLASS (klass);
3106 	object_class->finalize = e_cache_finalize;
3107 
3108 	klass->put_locked = e_cache_put_locked_default;
3109 	klass->remove_locked = e_cache_remove_locked_default;
3110 	klass->remove_all_locked = e_cache_remove_all_locked_default;
3111 	klass->clear_offline_changes_locked = e_cache_clear_offline_changes_locked_default;
3112 	klass->before_put = e_cache_before_put_default;
3113 	klass->before_remove = e_cache_before_remove_default;
3114 
3115 	signals[BEFORE_PUT] = g_signal_new (
3116 		"before-put",
3117 		G_OBJECT_CLASS_TYPE (klass),
3118 		G_SIGNAL_RUN_LAST,
3119 		G_STRUCT_OFFSET (ECacheClass, before_put),
3120 		e_cache_signals_accumulator,
3121 		NULL,
3122 		g_cclosure_marshal_generic,
3123 		G_TYPE_BOOLEAN, 7,
3124 		G_TYPE_STRING,
3125 		G_TYPE_STRING,
3126 		G_TYPE_STRING,
3127 		E_TYPE_CACHE_COLUMN_VALUES,
3128 		G_TYPE_BOOLEAN,
3129 		G_TYPE_CANCELLABLE,
3130 		G_TYPE_POINTER);
3131 
3132 	signals[BEFORE_REMOVE] = g_signal_new (
3133 		"before-remove",
3134 		G_OBJECT_CLASS_TYPE (klass),
3135 		G_SIGNAL_RUN_LAST,
3136 		G_STRUCT_OFFSET (ECacheClass, before_remove),
3137 		e_cache_signals_accumulator,
3138 		NULL,
3139 		g_cclosure_marshal_generic,
3140 		G_TYPE_BOOLEAN, 3,
3141 		G_TYPE_STRING,
3142 		G_TYPE_CANCELLABLE,
3143 		G_TYPE_POINTER);
3144 
3145 	signals[REVISION_CHANGED] = g_signal_new (
3146 		"revision-changed",
3147 		G_OBJECT_CLASS_TYPE (klass),
3148 		G_SIGNAL_RUN_LAST,
3149 		G_STRUCT_OFFSET (ECacheClass, revision_changed),
3150 		NULL,
3151 		NULL,
3152 		g_cclosure_marshal_generic,
3153 		G_TYPE_NONE, 0,
3154 		G_TYPE_NONE);
3155 
3156 	e_sqlite3_vfs_init ();
3157 }
3158 
3159 static void
e_cache_init(ECache * cache)3160 e_cache_init (ECache *cache)
3161 {
3162 	cache->priv = e_cache_get_instance_private (cache);
3163 
3164 	cache->priv->filename = NULL;
3165 	cache->priv->db = NULL;
3166 	cache->priv->cancellable = NULL;
3167 	cache->priv->in_transaction = 0;
3168 	cache->priv->revision_change_frozen = 0;
3169 	cache->priv->revision_counter = 0;
3170 	cache->priv->last_revision_time = 0;
3171 	cache->priv->needs_revision_change = FALSE;
3172 
3173 	g_rec_mutex_init (&cache->priv->lock);
3174 }
3175