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);
243 "Could not find a corresponding %s "
244 "property for state file tag 0x%x",
245 G_OBJECT_TYPE_NAME (object), tag);
247 g_value_unset (&value);
257 object_state_write (CamelObject *object,
260 GValue value = G_VALUE_INIT;
262 GParamSpec **properties;
263 guint ii, n_properties;
264 guint32 n_persistent = 0;
266 class = G_OBJECT_GET_CLASS (object);
267 properties = g_object_class_list_properties (class, &n_properties);
270 if (camel_file_util_encode_uint32 (fp, 1) == -1)
273 /* No meta-data items. */
274 if (camel_file_util_encode_uint32 (fp, 0) == -1)
277 /* Count persistent properties. */
278 for (ii = 0; ii < n_properties; ii++)
279 if (properties[ii]->flags & CAMEL_PARAM_PERSISTENT)
282 if (camel_file_util_encode_uint32 (fp, n_persistent) == -1)
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. */
290 for (ii = 0; ii < n_properties; ii++) {
291 GParamSpec *pspec = properties[ii];
292 guint32 tag, v_uint32;
294 if ((pspec->flags & CAMEL_PARAM_PERSISTENT) == 0)
297 g_value_init (&value, pspec->value_type);
299 g_object_get_property (
300 G_OBJECT (object), pspec->name, &value);
302 tag = pspec->param_id;
304 /* Record the GValue to the state file.
305 * XXX We currently only support booleans. */
306 switch (pspec->value_type) {
308 tag |= CAMEL_ARG_BOO;
309 v_uint32 = g_value_get_boolean (&value);
310 if (camel_file_util_encode_uint32 (fp, tag) == -1)
312 if (camel_file_util_encode_uint32 (fp, v_uint32) == -1)
316 g_warn_if_reached ();
320 g_value_unset (&value);
330 camel_object_class_init (CamelObjectClass *class)
332 GObjectClass *object_class;
334 g_type_class_add_private (class, sizeof (CamelObjectPrivate));
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;
341 class->state_read = object_state_read;
342 class->state_write = object_state_write;
345 * CamelObject:state-filename
347 * The file in which to store persistent property values for this
350 g_object_class_install_property (
353 g_param_spec_string (
356 "File containing persistent property values",
363 camel_object_init (CamelObject *object)
365 object->priv = CAMEL_OBJECT_GET_PRIVATE (object);
369 camel_error_quark (void)
371 static GQuark quark = 0;
373 if (G_UNLIKELY (quark == 0)) {
374 const gchar *string = "camel-error-quark";
375 quark = g_quark_from_static_string (string);
382 * camel_object_state_read:
383 * @object: a #CamelObject
385 * Read persistent object state from #CamelObject:state-filename.
387 * Returns: -1 on error.
390 camel_object_state_read (CamelObject *object)
392 CamelObjectClass *class;
393 const gchar *state_filename;
398 g_return_val_if_fail (CAMEL_IS_OBJECT (object), -1);
400 class = CAMEL_OBJECT_GET_CLASS (object);
402 state_filename = camel_object_get_state_filename (object);
403 if (state_filename == NULL)
406 fp = g_fopen (state_filename, "rb");
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);
418 * camel_object_state_write:
419 * @object: a #CamelObject
421 * Write persistent object state #CamelObject:state-filename.
423 * Returns: -1 on error.
426 camel_object_state_write (CamelObject *object)
428 CamelObjectClass *class;
429 const gchar *state_filename;
430 gchar *savename, *dirname;
434 g_return_val_if_fail (CAMEL_IS_OBJECT (object), -1);
436 class = CAMEL_OBJECT_GET_CLASS (object);
438 state_filename = camel_object_get_state_filename (object);
439 if (state_filename == NULL)
442 savename = camel_file_util_savename (state_filename);
444 dirname = g_path_get_dirname (savename);
445 g_mkdir_with_parents (dirname, 0700);
448 fp = g_fopen (savename, "wb");
450 if (fwrite (CAMEL_OBJECT_STATE_FILE_MAGIC, 4, 1, fp) == 1
451 && class->state_write (object, fp) == 0) {
452 if (fclose (fp) == 0) {
454 g_rename (savename, state_filename);
460 g_warning ("Could not save object state file to '%s': %s", savename, g_strerror (errno));
469 * camel_object_get_state_filename:
470 * @object: a #CamelObject
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.
476 * Returns: the name of the persistent property file
481 camel_object_get_state_filename (CamelObject *object)
483 g_return_val_if_fail (CAMEL_IS_OBJECT (object), NULL);
485 return object->priv->state_filename;
489 * camel_object_set_state_filename:
490 * @object: a #CamelObject
491 * @state_filename: path to a local file
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.
500 camel_object_set_state_filename (CamelObject *object,
501 const gchar *state_filename)
503 g_return_if_fail (CAMEL_IS_OBJECT (object));
505 if (g_strcmp0 (object->priv->state_filename, state_filename) == 0)
508 g_free (object->priv->state_filename);
509 object->priv->state_filename = g_strdup (state_filename);
511 g_object_notify (G_OBJECT (object), "state-filename");