Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-object.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
2  *
3  * Author:
4  *  Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20  * USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <glib/gstdio.h>
32
33 #include "camel-file-utils.h"
34 #include "camel-object.h"
35
36 #define d(x)
37
38 #define CAMEL_OBJECT_GET_PRIVATE(obj) \
39         (G_TYPE_INSTANCE_GET_PRIVATE \
40         ((obj), CAMEL_TYPE_OBJECT, CamelObjectPrivate))
41
42 struct _CamelObjectPrivate {
43         gchar *state_filename;
44 };
45
46 enum {
47         PROP_0,
48         PROP_STATE_FILENAME
49 };
50
51 G_DEFINE_ABSTRACT_TYPE (CamelObject, camel_object, G_TYPE_OBJECT)
52
53 /* State file for CamelObject data.
54  * Any later versions should only append data.
55  *
56  * version:uint32
57  *
58  * Version 0 of the file:
59  *
60  * version:uint32 = 0
61  * count:uint32                         -- count of meta-data items
62  * ( name:string value:string ) *count          -- meta-data items
63  *
64  * Version 1 of the file adds:
65  * count:uint32                                 -- count of persistent properties
66  * ( tag:uing32 value:tagtype ) *count          -- persistent properties
67  */
68
69 #define CAMEL_OBJECT_STATE_FILE_MAGIC "CLMD"
70
71 /* XXX This is a holdover from Camel's old homegrown type system.
72  *     CamelArg was a kind of primitive version of GObject properties.
73  *     The argument ID and data type were encoded into a 32-bit integer.
74  *     Unfortunately the encoding was also used in the binary state file
75  *     format, so we still need the secret decoder ring. */
76 enum camel_arg_t {
77         CAMEL_ARG_END = 0,
78         CAMEL_ARG_IGNORE = 1,   /* override/ignore an arg in-place */
79
80         CAMEL_ARG_FIRST = 1024, /* 1024 args reserved for arg system */
81
82         CAMEL_ARG_TYPE = 0xf0000000, /* type field for tags */
83         CAMEL_ARG_TAG = 0x0fffffff, /* tag field for args */
84
85         CAMEL_ARG_OBJ = 0x00000000, /* object */
86         CAMEL_ARG_INT = 0x10000000, /* gint */
87         CAMEL_ARG_DBL = 0x20000000, /* gdouble */
88         CAMEL_ARG_STR = 0x30000000, /* c string */
89         CAMEL_ARG_PTR = 0x40000000, /* ptr */
90         CAMEL_ARG_BOO = 0x50000000  /* bool */
91 };
92
93 #define CAMEL_ARGV_MAX (20)
94
95 static void
96 object_set_property (GObject *object,
97                      guint property_id,
98                      const GValue *value,
99                      GParamSpec *pspec)
100 {
101         switch (property_id) {
102                 case PROP_STATE_FILENAME:
103                         camel_object_set_state_filename (
104                                 CAMEL_OBJECT (object),
105                                 g_value_get_string (value));
106                         return;
107         }
108
109         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
110 }
111
112 static void
113 object_get_property (GObject *object,
114                      guint property_id,
115                      GValue *value,
116                      GParamSpec *pspec)
117 {
118         switch (property_id) {
119                 case PROP_STATE_FILENAME:
120                         g_value_set_string (
121                                 value, camel_object_get_state_filename (
122                                 CAMEL_OBJECT (object)));
123                         return;
124         }
125
126         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
127 }
128
129 static void
130 object_finalize (GObject *object)
131 {
132         CamelObjectPrivate *priv;
133
134         priv = CAMEL_OBJECT_GET_PRIVATE (object);
135
136         g_free (priv->state_filename);
137
138         /* Chain up to parent's finalize() method. */
139         G_OBJECT_CLASS (camel_object_parent_class)->finalize (object);
140 }
141
142 static gint
143 object_state_read (CamelObject *object,
144                    FILE *fp)
145 {
146         GValue value = G_VALUE_INIT;
147         GObjectClass *class;
148         GParamSpec **properties;
149         guint32 count, version;
150         guint ii, jj, n_properties;
151
152         if (camel_file_util_decode_uint32 (fp, &version) == -1)
153                 return -1;
154
155         if (version > 1)
156                 return -1;
157
158         if (camel_file_util_decode_uint32 (fp, &count) == -1)
159                 return -1;
160
161         /* XXX Camel no longer supports meta-data in state
162          *     files, so we're just eating dead data here. */
163         for (ii = 0; ii < count; ii++) {
164                 gchar *name = NULL;
165                 gchar *value = NULL;
166                 gboolean success;
167
168                 success =
169                         camel_file_util_decode_string (fp, &name) == 0 &&
170                         camel_file_util_decode_string (fp, &value) == 0;
171
172                 g_free (name);
173                 g_free (value);
174
175                 if (!success)
176                         return -1;
177         }
178
179         if (version == 0)
180                 return 0;
181
182         if (camel_file_util_decode_uint32 (fp, &count) == -1)
183                 return 0;
184
185         if (count == 0 || count > 1024)
186                 /* Maybe it was just version 0 afterall. */
187                 return 0;
188
189         count = MIN (count, CAMEL_ARGV_MAX);
190
191         class = G_OBJECT_GET_CLASS (object);
192         properties = g_object_class_list_properties (class, &n_properties);
193
194         for (ii = 0; ii < count; ii++) {
195                 gboolean property_set = FALSE;
196                 guint32 tag, v_uint32;
197
198                 if (camel_file_util_decode_uint32 (fp, &tag) == -1)
199                         goto exit;
200
201                 /* Record state file values into GValues.
202                  * XXX We currently only support booleans. */
203                 switch (tag & CAMEL_ARG_TYPE) {
204                         case CAMEL_ARG_BOO:
205                                 if (camel_file_util_decode_uint32 (fp, &v_uint32) == -1)
206                                         goto exit;
207                                 g_value_init (&value, G_TYPE_BOOLEAN);
208                                 g_value_set_boolean (&value, (gboolean) v_uint32);
209                                 break;
210                         default:
211                                 g_warn_if_reached ();
212                                 goto exit;
213                 }
214
215                 /* Now we have to match the legacy numeric CamelArg tag
216                  * value with a GObject property.  The GObject property
217                  * IDs have been set to the same legacy tag values, but
218                  * we have to access a private GParamSpec field to get
219                  * to them (pspec->param_id). */
220
221                 tag &= CAMEL_ARG_TAG;  /* filter out the type code */
222
223                 for (jj = 0; jj < n_properties; jj++) {
224                         GParamSpec *pspec = properties[jj];
225
226                         if (pspec->param_id != tag)
227                                 continue;
228
229                         /* Sanity check. */
230                         g_warn_if_fail (pspec->flags & CAMEL_PARAM_PERSISTENT);
231                         if ((pspec->flags & CAMEL_PARAM_PERSISTENT) == 0)
232                                 continue;
233
234                         g_object_set_property (
235                                 G_OBJECT (object), pspec->name, &value);
236
237                         property_set = TRUE;
238                         break;
239                 }
240
241                 if (!property_set)
242                         g_warning (
243                                 "Could not find a corresponding %s "
244                                 "property for state file tag 0x%x",
245                                 G_OBJECT_TYPE_NAME (object), tag);
246
247                 g_value_unset (&value);
248         }
249
250 exit:
251         g_free (properties);
252
253         return 0;
254 }
255
256 static gint
257 object_state_write (CamelObject *object,
258                     FILE *fp)
259 {
260         GValue value = G_VALUE_INIT;
261         GObjectClass *class;
262         GParamSpec **properties;
263         guint ii, n_properties;
264         guint32 n_persistent = 0;
265
266         class = G_OBJECT_GET_CLASS (object);
267         properties = g_object_class_list_properties (class, &n_properties);
268
269         /* Version = 1 */
270         if (camel_file_util_encode_uint32 (fp, 1) == -1)
271                 goto exit;
272
273         /* No meta-data items. */
274         if (camel_file_util_encode_uint32 (fp, 0) == -1)
275                 goto exit;
276
277         /* Count persistent properties. */
278         for (ii = 0; ii < n_properties; ii++)
279                 if (properties[ii]->flags & CAMEL_PARAM_PERSISTENT)
280                         n_persistent++;
281
282         if (camel_file_util_encode_uint32 (fp, n_persistent) == -1)
283                 goto exit;
284
285         /* Write a tag + value pair for each persistent property.
286          * Tags identify the property ID and data type; they're an
287          * artifact of CamelArgs.  The persistent GObject property
288          * IDs are set to match the legacy CamelArg tag values. */
289
290         for (ii = 0; ii < n_properties; ii++) {
291                 GParamSpec *pspec = properties[ii];
292                 guint32 tag, v_uint32;
293
294                 if ((pspec->flags & CAMEL_PARAM_PERSISTENT) == 0)
295                         continue;
296
297                 g_value_init (&value, pspec->value_type);
298
299                 g_object_get_property (
300                         G_OBJECT (object), pspec->name, &value);
301
302                 tag = pspec->param_id;
303
304                 /* Record the GValue to the state file.
305                  * XXX We currently only support booleans. */
306                 switch (pspec->value_type) {
307                         case G_TYPE_BOOLEAN:
308                                 tag |= CAMEL_ARG_BOO;
309                                 v_uint32 = g_value_get_boolean (&value);
310                                 if (camel_file_util_encode_uint32 (fp, tag) == -1)
311                                         goto exit;
312                                 if (camel_file_util_encode_uint32 (fp, v_uint32) == -1)
313                                         goto exit;
314                                 break;
315                         default:
316                                 g_warn_if_reached ();
317                                 goto exit;
318                 }
319
320                 g_value_unset (&value);
321         }
322
323 exit:
324         g_free (properties);
325
326         return 0;
327 }
328
329 static void
330 camel_object_class_init (CamelObjectClass *class)
331 {
332         GObjectClass *object_class;
333
334         g_type_class_add_private (class, sizeof (CamelObjectPrivate));
335
336         object_class = G_OBJECT_CLASS (class);
337         object_class->set_property = object_set_property;
338         object_class->get_property = object_get_property;
339         object_class->finalize = object_finalize;
340
341         class->state_read = object_state_read;
342         class->state_write = object_state_write;
343
344         /**
345          * CamelObject:state-filename
346          *
347          * The file in which to store persistent property values for this
348          * instance.
349          **/
350         g_object_class_install_property (
351                 object_class,
352                 PROP_STATE_FILENAME,
353                 g_param_spec_string (
354                         "state-filename",
355                         "State Filename",
356                         "File containing persistent property values",
357                         NULL,
358                         G_PARAM_READWRITE |
359                         G_PARAM_CONSTRUCT));
360 }
361
362 static void
363 camel_object_init (CamelObject *object)
364 {
365         object->priv = CAMEL_OBJECT_GET_PRIVATE (object);
366 }
367
368 GQuark
369 camel_error_quark (void)
370 {
371         static GQuark quark = 0;
372
373         if (G_UNLIKELY (quark == 0)) {
374                 const gchar *string = "camel-error-quark";
375                 quark = g_quark_from_static_string (string);
376         }
377
378         return quark;
379 }
380
381 /**
382  * camel_object_state_read:
383  * @object: a #CamelObject
384  *
385  * Read persistent object state from #CamelObject:state-filename.
386  *
387  * Returns: -1 on error.
388  **/
389 gint
390 camel_object_state_read (CamelObject *object)
391 {
392         CamelObjectClass *class;
393         const gchar *state_filename;
394         gint res = -1;
395         FILE *fp;
396         gchar magic[4];
397
398         g_return_val_if_fail (CAMEL_IS_OBJECT (object), -1);
399
400         class = CAMEL_OBJECT_GET_CLASS (object);
401
402         state_filename = camel_object_get_state_filename (object);
403         if (state_filename == NULL)
404                 return 0;
405
406         fp = g_fopen (state_filename, "rb");
407         if (fp != NULL) {
408                 if (fread (magic, 4, 1, fp) == 1
409                     && memcmp (magic, CAMEL_OBJECT_STATE_FILE_MAGIC, 4) == 0)
410                         res = class->state_read (object, fp);
411                 fclose (fp);
412         }
413
414         return res;
415 }
416
417 /**
418  * camel_object_state_write:
419  * @object: a #CamelObject
420  *
421  * Write persistent object state #CamelObject:state-filename.
422  *
423  * Returns: -1 on error.
424  **/
425 gint
426 camel_object_state_write (CamelObject *object)
427 {
428         CamelObjectClass *class;
429         const gchar *state_filename;
430         gchar *savename, *dirname;
431         gint res = -1;
432         FILE *fp;
433
434         g_return_val_if_fail (CAMEL_IS_OBJECT (object), -1);
435
436         class = CAMEL_OBJECT_GET_CLASS (object);
437
438         state_filename = camel_object_get_state_filename (object);
439         if (state_filename == NULL)
440                 return 0;
441
442         savename = camel_file_util_savename (state_filename);
443
444         dirname = g_path_get_dirname (savename);
445         g_mkdir_with_parents (dirname, 0700);
446         g_free (dirname);
447
448         fp = g_fopen (savename, "wb");
449         if (fp != NULL) {
450                 if (fwrite (CAMEL_OBJECT_STATE_FILE_MAGIC, 4, 1, fp) == 1
451                     && class->state_write (object, fp) == 0) {
452                         if (fclose (fp) == 0) {
453                                 res = 0;
454                                 g_rename (savename, state_filename);
455                         }
456                 } else {
457                         fclose (fp);
458                 }
459         } else {
460                 g_warning ("Could not save object state file to '%s': %s", savename, g_strerror (errno));
461         }
462
463         g_free (savename);
464
465         return res;
466 }
467
468 /**
469  * camel_object_get_state_filename:
470  * @object: a #CamelObject
471  *
472  * Returns the name of the file in which persistent property values for
473  * @object are stored.  The file is used by camel_object_state_write()
474  * and camel_object_state_read() to save and restore object state.
475  *
476  * Returns: the name of the persistent property file
477  *
478  * Since: 2.32
479  **/
480 const gchar *
481 camel_object_get_state_filename (CamelObject *object)
482 {
483         g_return_val_if_fail (CAMEL_IS_OBJECT (object), NULL);
484
485         return object->priv->state_filename;
486 }
487
488 /**
489  * camel_object_set_state_filename:
490  * @object: a #CamelObject
491  * @state_filename: path to a local file
492  *
493  * Sets the name of the file in which persistent property values for
494  * @object are stored.  The file is used by camel_object_state_write()
495  * and camel_object_state_read() to save and restore object state.
496  *
497  * Since: 2.32
498  **/
499 void
500 camel_object_set_state_filename (CamelObject *object,
501                                  const gchar *state_filename)
502 {
503         g_return_if_fail (CAMEL_IS_OBJECT (object));
504
505         if (g_strcmp0 (object->priv->state_filename, state_filename) == 0)
506                 return;
507
508         g_free (object->priv->state_filename);
509         object->priv->state_filename = g_strdup (state_filename);
510
511         g_object_notify (G_OBJECT (object), "state-filename");
512 }