1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *
4 * Michael Zucchi <notzed@ximian.com>
6 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
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.
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
31 #include <glib/gstdio.h>
33 #include "camel-file-utils.h"
34 #include "camel-object.h"
38 #define CAMEL_OBJECT_GET_PRIVATE(obj) \
39 (G_TYPE_INSTANCE_GET_PRIVATE \
40 ((obj), CAMEL_TYPE_OBJECT, CamelObjectPrivate))
42 struct _CamelObjectPrivate {
43 gchar *state_filename;
51 G_DEFINE_ABSTRACT_TYPE (CamelObject, camel_object, G_TYPE_OBJECT)
53 /* State file for CamelObject data.
54 * Any later versions should only append data.
58 * Version 0 of the file:
61 * count:uint32 -- count of meta-data items
62 * ( name:string value:string ) *count -- meta-data items
64 * Version 1 of the file adds:
65 * count:uint32 -- count of persistent properties
66 * ( tag:uing32 value:tagtype ) *count -- persistent properties
69 #define CAMEL_OBJECT_STATE_FILE_MAGIC "CLMD"
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. */
78 CAMEL_ARG_IGNORE = 1, /* override/ignore an arg in-place */
80 CAMEL_ARG_FIRST = 1024, /* 1024 args reserved for arg system */
82 CAMEL_ARG_TYPE = 0xf0000000, /* type field for tags */
83 CAMEL_ARG_TAG = 0x0fffffff, /* tag field for args */
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 */
93 #define CAMEL_ARGV_MAX (20)
96 object_set_property (GObject *object,
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));
109 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
113 object_get_property (GObject *object,
118 switch (property_id) {
119 case PROP_STATE_FILENAME:
121 value, camel_object_get_state_filename (
122 CAMEL_OBJECT (object)));
126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
130 object_finalize (GObject *object)
132 CamelObjectPrivate *priv;
134 priv = CAMEL_OBJECT_GET_PRIVATE (object);
136 g_free (priv->state_filename);
138 /* Chain up to parent's finalize() method. */
139 G_OBJECT_CLASS (camel_object_parent_class)->finalize (object);
143 object_state_read (CamelObject *object,
146 GValue value = G_VALUE_INIT;
148 GParamSpec **properties;
149 guint32 count, version;
150 guint ii, jj, n_properties;
152 if (camel_file_util_decode_uint32 (fp, &version) == -1)
158 if (camel_file_util_decode_uint32 (fp, &count) == -1)
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++) {
169 camel_file_util_decode_string (fp, &name) == 0 &&
170 camel_file_util_decode_string (fp, &value) == 0;
182 if (camel_file_util_decode_uint32 (fp, &count) == -1)
185 if (count == 0 || count > 1024)
186 /* Maybe it was just version 0 afterall. */
189 count = MIN (count, CAMEL_ARGV_MAX);
191 class = G_OBJECT_GET_CLASS (object);
192 properties = g_object_class_list_properties (class, &n_properties);
194 for (ii = 0; ii < count; ii++) {
195 gboolean property_set = FALSE;
196 guint32 tag, v_uint32;
198 if (camel_file_util_decode_uint32 (fp, &tag) == -1)
201 /* Record state file values into GValues.
202 * XXX We currently only support booleans. */
203 switch (tag & CAMEL_ARG_TYPE) {
205 if (camel_file_util_decode_uint32 (fp, &v_uint32) == -1)
207 g_value_init (&value, G_TYPE_BOOLEAN);
208 g_value_set_boolean (&value, (gboolean) v_uint32);
211 g_warn_if_reached ();
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). */
221 tag &= CAMEL_ARG_TAG; /* filter out the type code */
223 for (jj = 0; jj < n_properties; jj++) {
224 GParamSpec *pspec = properties[jj];
226 if (pspec->param_id != tag)
230 g_warn_if_fail (pspec->flags & CAMEL_PARAM_PERSISTENT);
231 if ((pspec->flags & CAMEL_PARAM_PERSISTENT) == 0)
234 g_object_set_property (
235 G_OBJECT (object), pspec->name, &value);
241 /* XXX This tag was used by the old IMAP backend.
242 * It may still show up in accounts that were
243 * migrated from IMAP to IMAPX. Silence the
250 "Could not find a corresponding %s "
251 "property for state file tag 0x%x",
252 G_OBJECT_TYPE_NAME (object), tag);
254 g_value_unset (&value);
264 object_state_write (CamelObject *object,
267 GValue value = G_VALUE_INIT;
269 GParamSpec **properties;
270 guint ii, n_properties;
271 guint32 n_persistent = 0;
273 class = G_OBJECT_GET_CLASS (object);
274 properties = g_object_class_list_properties (class, &n_properties);
277 if (camel_file_util_encode_uint32 (fp, 1) == -1)
280 /* No meta-data items. */
281 if (camel_file_util_encode_uint32 (fp, 0) == -1)
284 /* Count persistent properties. */
285 for (ii = 0; ii < n_properties; ii++)
286 if (properties[ii]->flags & CAMEL_PARAM_PERSISTENT)
289 if (camel_file_util_encode_uint32 (fp, n_persistent) == -1)
292 /* Write a tag + value pair for each persistent property.
293 * Tags identify the property ID and data type; they're an
294 * artifact of CamelArgs. The persistent GObject property
295 * IDs are set to match the legacy CamelArg tag values. */
297 for (ii = 0; ii < n_properties; ii++) {
298 GParamSpec *pspec = properties[ii];
299 guint32 tag, v_uint32;
301 if ((pspec->flags & CAMEL_PARAM_PERSISTENT) == 0)
304 g_value_init (&value, pspec->value_type);
306 g_object_get_property (
307 G_OBJECT (object), pspec->name, &value);
309 tag = pspec->param_id;
311 /* Record the GValue to the state file.
312 * XXX We currently only support booleans. */
313 switch (pspec->value_type) {
315 tag |= CAMEL_ARG_BOO;
316 v_uint32 = g_value_get_boolean (&value);
317 if (camel_file_util_encode_uint32 (fp, tag) == -1)
319 if (camel_file_util_encode_uint32 (fp, v_uint32) == -1)
323 g_warn_if_reached ();
327 g_value_unset (&value);
337 camel_object_class_init (CamelObjectClass *class)
339 GObjectClass *object_class;
341 g_type_class_add_private (class, sizeof (CamelObjectPrivate));
343 object_class = G_OBJECT_CLASS (class);
344 object_class->set_property = object_set_property;
345 object_class->get_property = object_get_property;
346 object_class->finalize = object_finalize;
348 class->state_read = object_state_read;
349 class->state_write = object_state_write;
352 * CamelObject:state-filename
354 * The file in which to store persistent property values for this
357 g_object_class_install_property (
360 g_param_spec_string (
363 "File containing persistent property values",
370 camel_object_init (CamelObject *object)
372 object->priv = CAMEL_OBJECT_GET_PRIVATE (object);
375 G_DEFINE_QUARK (camel-error-quark, camel_error)
378 * camel_object_state_read:
379 * @object: a #CamelObject
381 * Read persistent object state from #CamelObject:state-filename.
383 * Returns: -1 on error.
386 camel_object_state_read (CamelObject *object)
388 CamelObjectClass *class;
389 const gchar *state_filename;
394 g_return_val_if_fail (CAMEL_IS_OBJECT (object), -1);
396 class = CAMEL_OBJECT_GET_CLASS (object);
398 state_filename = camel_object_get_state_filename (object);
399 if (state_filename == NULL)
402 fp = g_fopen (state_filename, "rb");
404 if (fread (magic, 4, 1, fp) == 1
405 && memcmp (magic, CAMEL_OBJECT_STATE_FILE_MAGIC, 4) == 0)
406 res = class->state_read (object, fp);
414 * camel_object_state_write:
415 * @object: a #CamelObject
417 * Write persistent object state #CamelObject:state-filename.
419 * Returns: -1 on error.
422 camel_object_state_write (CamelObject *object)
424 CamelObjectClass *class;
425 const gchar *state_filename;
426 gchar *savename, *dirname;
430 g_return_val_if_fail (CAMEL_IS_OBJECT (object), -1);
432 class = CAMEL_OBJECT_GET_CLASS (object);
434 state_filename = camel_object_get_state_filename (object);
435 if (state_filename == NULL)
438 savename = camel_file_util_savename (state_filename);
440 dirname = g_path_get_dirname (savename);
441 g_mkdir_with_parents (dirname, 0700);
444 fp = g_fopen (savename, "wb");
446 if (fwrite (CAMEL_OBJECT_STATE_FILE_MAGIC, 4, 1, fp) == 1
447 && class->state_write (object, fp) == 0) {
448 if (fclose (fp) == 0) {
450 g_rename (savename, state_filename);
456 g_warning ("Could not save object state file to '%s': %s", savename, g_strerror (errno));
465 * camel_object_get_state_filename:
466 * @object: a #CamelObject
468 * Returns the name of the file in which persistent property values for
469 * @object are stored. The file is used by camel_object_state_write()
470 * and camel_object_state_read() to save and restore object state.
472 * Returns: the name of the persistent property file
477 camel_object_get_state_filename (CamelObject *object)
479 g_return_val_if_fail (CAMEL_IS_OBJECT (object), NULL);
481 return object->priv->state_filename;
485 * camel_object_set_state_filename:
486 * @object: a #CamelObject
487 * @state_filename: path to a local file
489 * Sets the name of the file in which persistent property values for
490 * @object are stored. The file is used by camel_object_state_write()
491 * and camel_object_state_read() to save and restore object state.
496 camel_object_set_state_filename (CamelObject *object,
497 const gchar *state_filename)
499 g_return_if_fail (CAMEL_IS_OBJECT (object));
501 if (g_strcmp0 (object->priv->state_filename, state_filename) == 0)
504 g_free (object->priv->state_filename);
505 object->priv->state_filename = g_strdup (state_filename);
507 g_object_notify (G_OBJECT (object), "state-filename");