Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserver / e-file-cache.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* e-file-cache.c
3  *
4  * Copyright (C) 2003 Novell, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * Authors: Rodrigo Moya <rodrigo@ximian.com>
20  */
21
22 #include <config.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include <glib.h>
27 #include <glib/gstdio.h>
28
29 #include "e-file-cache.h"
30 #include "e-data-server-util.h"
31 #include "e-xml-hash-utils.h"
32
33 struct _EFileCachePrivate {
34         char *filename;
35         EXmlHash *xml_hash;
36         gboolean dirty;
37         gboolean frozen;
38 };
39
40 /* Property IDs */
41 enum {
42         PROP_0,
43         PROP_FILENAME
44 };
45
46 G_DEFINE_TYPE (EFileCache, e_file_cache, G_TYPE_OBJECT);
47
48 static void
49 e_file_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
50 {
51         EFileCache *cache;
52         EFileCachePrivate *priv;
53         char *dirname;
54         int result;
55
56         cache = E_FILE_CACHE (object);
57         priv = cache->priv;
58
59         /* FIXME: the property is being set twice. Need to investigate
60          * why and fix. Until then, we just return when called the 
61          * second time*/
62         if (priv->filename)
63                 return;
64
65         switch (property_id) {
66         case PROP_FILENAME :
67                 /* make sure the directory for the cache exists */
68                 priv->filename = g_strdup ( g_value_get_string (value));
69                 dirname = g_path_get_dirname (priv->filename);
70                 result = g_mkdir_with_parents (dirname, 0700);
71                 g_free (dirname);
72                 if (result != 0)
73                         break;
74
75                 if (priv->xml_hash)
76                         e_xmlhash_destroy (priv->xml_hash);
77                 priv->xml_hash = e_xmlhash_new (g_value_get_string (value));
78
79                 /* if opening the cache file fails, remove it and try again */
80                 if (!priv->xml_hash) {
81                         g_unlink (g_value_get_string (value));
82                         priv->xml_hash = e_xmlhash_new (g_value_get_string (value));
83                         if (priv->xml_hash) {
84                                 g_message (G_STRLOC ": could not open not re-create cache file %s",
85                                            g_value_get_string (value));
86                         }
87                 }
88                 break;
89         default :
90                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
91         }
92 }
93
94 static void
95 e_file_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
96 {
97         EFileCache *cache;
98         EFileCachePrivate *priv;
99
100         cache = E_FILE_CACHE (object);
101         priv = cache->priv;
102
103         switch (property_id) {
104         case PROP_FILENAME :
105                 g_value_set_string (value, priv->filename);
106                 break;
107         default :
108                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109         }
110 }
111
112 static void
113 e_file_cache_finalize (GObject *object)
114 {
115         EFileCache *cache;
116         EFileCachePrivate *priv;
117
118         cache = E_FILE_CACHE (object);
119         priv = cache->priv;
120
121         if (priv) {
122                 if (priv->filename) {
123                         g_free (priv->filename);
124                         priv->filename = NULL;
125                 }
126
127                 if (priv->xml_hash) {
128                         e_xmlhash_destroy (priv->xml_hash);
129                         priv->xml_hash = NULL;
130                 }
131
132                 g_free (priv);
133                 cache->priv = NULL;
134         }
135
136         G_OBJECT_CLASS (e_file_cache_parent_class)->finalize (object);
137 }
138
139 static void
140 e_file_cache_class_init (EFileCacheClass *klass)
141 {
142         GObjectClass *object_class;
143
144         object_class = G_OBJECT_CLASS (klass);
145         object_class->finalize = e_file_cache_finalize;
146         object_class->set_property = e_file_cache_set_property;
147         object_class->get_property = e_file_cache_get_property;
148
149         g_object_class_install_property (object_class, PROP_FILENAME,
150                                          g_param_spec_string ("filename", NULL, NULL, "",
151                                                               G_PARAM_READABLE | G_PARAM_WRITABLE
152                                                               | G_PARAM_CONSTRUCT_ONLY));
153 }
154
155 static void
156 e_file_cache_init (EFileCache *cache)
157 {
158         EFileCachePrivate *priv;
159
160         priv = g_new0 (EFileCachePrivate, 1);
161         priv->dirty = FALSE;
162         priv->frozen = FALSE;
163         cache->priv = priv;
164 }
165
166 /**
167  * e_file_cache_new
168  * @filename: filename where the cache is kept.
169  *
170  * Creates a new #EFileCache object, which implements a cache of
171  * objects, very useful for remote backends.
172  *
173  * Return value: The newly created object.
174  */
175 EFileCache *
176 e_file_cache_new (const char *filename)
177 {
178         EFileCache *cache;
179
180         cache = g_object_new (E_TYPE_FILE_CACHE, "filename", filename, NULL);
181
182         return cache;
183 }
184
185 /**
186  * e_file_cache_remove:
187  * @cache: A #EFileCache object.
188  *
189  * Remove the cache from disk.
190  *
191  * Returns: TRUE if successful, FALSE otherwise.
192  */
193 gboolean
194 e_file_cache_remove (EFileCache *cache)
195 {
196         EFileCachePrivate *priv;
197
198         g_return_val_if_fail (E_IS_FILE_CACHE (cache), FALSE);
199
200         priv = cache->priv;
201
202         if (priv->filename) {
203                 char *dirname, *full_path;
204                 const char *fname;
205                 GDir *dir;
206                 gboolean success;
207
208                 /* remove all files in the directory */
209                 dirname = g_path_get_dirname (priv->filename);
210                 dir = g_dir_open (dirname, 0, NULL);
211                 if (dir) {
212                         while ((fname = g_dir_read_name (dir))) {
213                                 full_path = g_build_filename (dirname, fname, NULL);
214                                 if (g_unlink (full_path) != 0) {
215                                         g_free (full_path);
216                                         g_free (dirname);
217                                         g_dir_close (dir);
218
219                                         return FALSE;
220                                 }
221
222                                 g_free (full_path);
223                         }
224
225                         g_dir_close (dir);
226                 }
227
228                 /* remove the directory itself */
229                 success = g_rmdir (dirname) == 0;
230
231                 /* free all memory */
232                 g_free (dirname);
233                 g_free (priv->filename);
234                 priv->filename = NULL;
235
236                 e_xmlhash_destroy (priv->xml_hash);
237                 priv->xml_hash = NULL;
238
239                 return success;
240         }
241
242         return TRUE;
243 }
244
245 static void
246 add_key_to_list (const char *key, const char *value, gpointer user_data)
247 {
248         GList **keys = user_data;
249
250         *keys = g_list_append (*keys, (char *) key);
251 }
252
253 /**
254  * e_file_cache_clean:
255  * @cache: A #EFileCache object.
256  *
257  * Clean up the cache's contents.
258  *
259  * Returns: TRUE if successful, FALSE otherwise.
260  */
261 gboolean
262 e_file_cache_clean (EFileCache *cache)
263 {
264         EFileCachePrivate *priv;
265         GList *keys = NULL;
266
267         g_return_val_if_fail (E_IS_FILE_CACHE (cache), FALSE);
268
269         priv = cache->priv;
270
271         e_xmlhash_foreach_key (priv->xml_hash, (EXmlHashFunc) add_key_to_list, &keys);
272         while (keys != NULL) {
273                 e_file_cache_remove_object (cache, (const char *) keys->data);
274                 keys = g_list_remove (keys, keys->data);
275         }
276
277         return TRUE;
278 }
279
280 typedef struct {
281         const char *key;
282         gboolean found;
283         const char *found_value;
284 } CacheFindData;
285
286 static void
287 find_object_in_hash (gpointer key, gpointer value, gpointer user_data)
288 {
289         CacheFindData *find_data = user_data;
290
291         if (find_data->found)
292                 return;
293
294         if (!strcmp (find_data->key, (const char *) key)) {
295                 find_data->found = TRUE;
296                 find_data->found_value = (const char *) value;
297         }
298 }
299
300 /**
301  * e_file_cache_get_object:
302  */
303 const char *
304 e_file_cache_get_object (EFileCache *cache, const char *key)
305 {
306         CacheFindData find_data;
307         EFileCachePrivate *priv;
308
309         g_return_val_if_fail (E_IS_FILE_CACHE (cache), NULL);
310         g_return_val_if_fail (key != NULL, NULL);
311
312         priv = cache->priv;
313
314         find_data.key = key;
315         find_data.found = FALSE;
316         find_data.found_value = NULL;
317
318         e_xmlhash_foreach_key (priv->xml_hash, (EXmlHashFunc) find_object_in_hash, &find_data);
319
320         return find_data.found_value;
321 }
322
323 static void
324 add_object_to_list (const char *key, const char *value, gpointer user_data)
325 {
326         GSList **list = user_data;
327
328         *list = g_slist_prepend (*list, (char *) value);
329 }
330
331 /**
332  * e_file_cache_get_objects:
333  */
334 GSList *
335 e_file_cache_get_objects (EFileCache *cache)
336 {
337         EFileCachePrivate *priv;
338         GSList *list = NULL;
339
340         g_return_val_if_fail (E_IS_FILE_CACHE (cache), NULL);
341
342         priv = cache->priv;
343
344         e_xmlhash_foreach_key (priv->xml_hash, (EXmlHashFunc) add_object_to_list, &list);
345
346         return list;
347 }
348
349 /**
350  * e_file_cache_get_keys:
351  */
352 GSList *
353 e_file_cache_get_keys (EFileCache *cache)
354 {
355         EFileCachePrivate *priv;
356         GSList *list = NULL;
357
358         g_return_val_if_fail (E_IS_FILE_CACHE (cache), NULL);
359
360         priv = cache->priv;
361
362         e_xmlhash_foreach_key (priv->xml_hash, (EXmlHashFunc) add_key_to_list, &list);
363
364         return list;
365 }
366
367 /**
368  * e_file_cache_add_object:
369  */
370 gboolean
371 e_file_cache_add_object (EFileCache *cache, const char *key, const char *value)
372 {
373         EFileCachePrivate *priv;
374
375         g_return_val_if_fail (E_IS_FILE_CACHE (cache), FALSE);
376         g_return_val_if_fail (key != NULL, FALSE);
377
378         priv = cache->priv;
379
380         if (e_file_cache_get_object (cache, key))
381                 return FALSE;
382
383         e_xmlhash_add (priv->xml_hash, key, value);
384         if (priv->frozen)
385                 priv->dirty = TRUE;
386         else {
387                 e_xmlhash_write (priv->xml_hash);
388                 priv->dirty = FALSE;
389         }
390
391         return TRUE;
392 }
393
394 /**
395  * e_file_cache_replace_object:
396  */
397 gboolean
398 e_file_cache_replace_object (EFileCache *cache, const char *key, const char *new_value)
399 {
400         EFileCachePrivate *priv;
401
402         g_return_val_if_fail (E_IS_FILE_CACHE (cache), FALSE);
403         g_return_val_if_fail (key != NULL, FALSE);
404
405         priv = cache->priv;
406
407         if (!e_file_cache_get_object (cache, key))
408                 return FALSE;
409
410         if (!e_file_cache_remove_object (cache, key))
411                 return FALSE;
412
413         return e_file_cache_add_object (cache, key, new_value);
414 }
415
416 /**
417  * e_file_cache_remove_object:
418  */
419 gboolean
420 e_file_cache_remove_object (EFileCache *cache, const char *key)
421 {
422         EFileCachePrivate *priv;
423
424         g_return_val_if_fail (E_IS_FILE_CACHE (cache), FALSE);
425         g_return_val_if_fail (key != NULL, FALSE);
426
427         priv = cache->priv;
428
429         if (!e_file_cache_get_object (cache, key))
430                 return FALSE;
431
432         e_xmlhash_remove (priv->xml_hash, key);
433         if (priv->frozen)
434                 priv->dirty = TRUE;
435         else {
436                 e_xmlhash_write (priv->xml_hash);
437                 priv->dirty = FALSE;
438         }
439
440         return TRUE;
441 }
442
443 /**
444  * e_file_cache_freeze_changes:
445  * @cache: An #EFileCache object.
446  *
447  * Disables temporarily all writes to disk for the given cache object.
448  */
449 void
450 e_file_cache_freeze_changes (EFileCache *cache)
451 {
452         EFileCachePrivate *priv;
453
454         g_return_if_fail (E_IS_FILE_CACHE (cache));
455
456         priv = cache->priv;
457
458         priv->frozen = TRUE;
459 }
460
461 /**
462  * e_file_cache_thaw_changes:
463  * @cache: An #EFileCache object.
464  *
465  * Enables again writes to disk on every change.
466  */
467 void
468 e_file_cache_thaw_changes (EFileCache *cache)
469 {
470         EFileCachePrivate *priv;
471
472         g_return_if_fail (E_IS_FILE_CACHE (cache));
473
474         priv = cache->priv;
475
476         priv->frozen = FALSE;
477         if (priv->dirty) {
478                 e_xmlhash_write (priv->xml_hash);
479                 priv->dirty = FALSE;
480         }
481 }
482
483 /**
484  * e_file_cache_get_filename:
485  * @cache: A %EFileCache object.
486  *
487  * Gets the name of the file where the cache is being stored.
488  *
489  * Return value: The name of the cache.
490  */
491 const char *
492 e_file_cache_get_filename (EFileCache *cache)
493 {
494         g_return_val_if_fail (E_IS_FILE_CACHE (cache), NULL);
495         return (const char *) cache->priv->filename;
496 }
497