2 * e-source-camel-provider.c
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
20 * SECTION: e-source-camel
21 * @include: libedataserver/e-source-camel.h
22 * @short_description: #ESource extension for #CamelSettings
24 * #ESourceCamel itself is abstract. Its sole function is to
25 * bridge #GObject properties from the #CamelSettings framework to the
26 * #ESource framework. It does this by procedurally registering an
27 * #ESourceCamel subtype for each available #CamelService subtype,
28 * and then registering #GObject properties to proxy the properties in the
29 * corresponding #CamelSettings subtype. The #ESourceCamel owns an
30 * instance of the appropriate #CamelSettings subtype, and redirects all
31 * get/set operations on its own #GObject properties to its #CamelSettings
32 * instance. The #CamelSettings instance, now fully initialized from a key
33 * file, can then be inserted into a new #CamelService instance using
34 * camel_service_set_settings().
36 * Ultimately, this is all just implementation detail for glueing two
37 * unrelated class hierarchies together. If you need to access provider
38 * specific settings, use the #CamelSettings API, not this.
41 #include "e-source-camel.h"
44 #include <glib/gprintf.h>
46 #include <libedataserver/e-data-server-util.h>
47 #include <libedataserver/e-source-authentication.h>
48 #include <libedataserver/e-source-offline.h>
49 #include <libedataserver/e-source-security.h>
51 #define E_SOURCE_CAMEL_GET_PRIVATE(obj) \
52 (G_TYPE_INSTANCE_GET_PRIVATE \
53 ((obj), E_TYPE_SOURCE_CAMEL, ESourceCamelPrivate))
55 struct _ESourceCamelPrivate {
56 CamelSettings *settings;
66 const gchar *extension_name;
67 const gchar *extension_property_name;
68 const gchar *settings_property_name;
69 GBindingTransformFunc extension_to_settings;
70 GBindingTransformFunc settings_to_extension;
74 transform_none_to_null (GBinding *binding,
75 const GValue *source_value,
79 const gchar *v_string;
81 /* XXX Camel doesn't understand ESource's convention of using
82 * "none" to represent no value, instead of NULL or empty
83 * strings. So convert "none" to NULL for Camel. */
85 v_string = g_value_get_string (source_value);
87 if (g_strcmp0 (v_string, "none") == 0)
90 g_value_set_string (target_value, v_string);
95 static BindingData bindings[] = {
97 { E_SOURCE_EXTENSION_AUTHENTICATION,
100 { E_SOURCE_EXTENSION_AUTHENTICATION,
101 "method", "auth-mechanism",
102 transform_none_to_null,
105 { E_SOURCE_EXTENSION_AUTHENTICATION,
108 { E_SOURCE_EXTENSION_AUTHENTICATION,
111 { E_SOURCE_EXTENSION_OFFLINE,
112 "stay-synchronized", "stay-synchronized" },
114 { E_SOURCE_EXTENSION_SECURITY,
115 "method", "security-method",
116 e_binding_transform_enum_nick_to_value,
117 e_binding_transform_enum_value_to_nick }
120 G_DEFINE_ABSTRACT_TYPE (
123 E_TYPE_SOURCE_EXTENSION)
125 /* XXX A function like this belongs in GObject. I may yet propose it,
126 * GParamSpecClass still has some reserved slots. This fiddles with
127 * GParamSpec fields that are supposed to be private to GObject, but
128 * I have no other choice.
130 * XXX Historical note, originally I tried (ab)using override properties
131 * in ESourceCamel, which redirected to the equivalent CamelSettings
132 * property. Seemed to work at first, and I was proud of my clever
133 * hack, but it turns out g_object_class_list_properties() excludes
134 * override properties. So the ESourceCamel properties were being
135 * skipped in source_load_from_key_file() (e-source.c). */
137 param_spec_clone (GParamSpec *pspec)
142 /* Query the instance size. */
143 g_type_query (G_PARAM_SPEC_TYPE (pspec), &query);
145 /* Start with a memcpy()'d buffer. */
146 clone = g_slice_alloc0 (query.instance_size);
147 memcpy (clone, pspec, query.instance_size);
149 /* This sort of mimics g_param_spec_init(). */
151 #define PARAM_FLOATING_FLAG 0x2 /* from gparam.c */
152 g_datalist_set_flags (&clone->qdata, PARAM_FLOATING_FLAG);
153 clone->ref_count = 1;
155 /* Clear the owner_type. */
156 clone->owner_type = G_TYPE_INVALID;
158 /* Clear the param_id. */
161 /* This sort of mimics g_param_spec_internal(). */
163 /* Param name should already be canonicalized and interned. */
165 /* Always copy the nickname. */
166 clone->flags &= ~G_PARAM_STATIC_NICK;
167 clone->_nick = g_strdup (g_param_spec_get_nick (pspec));
169 /* Always copy the blurb. */
170 clone->flags &= ~G_PARAM_STATIC_BLURB;
171 clone->_blurb = g_strdup (g_param_spec_get_blurb (pspec));
173 /* Handle special cases. */
175 if (G_IS_PARAM_SPEC_STRING (clone)) {
176 GParamSpecString *clone_s;
178 clone_s = (GParamSpecString *) clone;
179 clone_s->default_value = g_strdup (clone_s->default_value);
182 /* Some types we don't handle but shouldn't need to. */
183 g_warn_if_fail (!G_IS_PARAM_SPEC_VALUE_ARRAY (clone));
184 g_warn_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (clone));
185 g_warn_if_fail (!G_IS_PARAM_SPEC_VARIANT (clone));
191 subclass_get_binding_index (GParamSpec *settings_property)
195 /* Return the index in the bindings list for the given
196 * CamelSettings property specification, or else -1. */
198 for (ii = 0; ii < G_N_ELEMENTS (bindings); ii++) {
199 const gchar *property_name;
201 property_name = bindings[ii].settings_property_name;
202 if (g_strcmp0 (settings_property->name, property_name) == 0)
210 subclass_set_property (GObject *object,
212 const GValue *src_value,
215 ESourceCamel *extension;
219 extension = E_SOURCE_CAMEL (object);
220 value_array = extension->priv->value_array;
222 dst_value = &g_array_index (value_array, GValue, property_id - 1);
223 g_value_copy (src_value, dst_value);
227 subclass_get_property (GObject *object,
232 ESourceCamel *extension;
236 extension = E_SOURCE_CAMEL (object);
237 value_array = extension->priv->value_array;
239 src_value = &g_array_index (value_array, GValue, property_id - 1);
240 g_value_copy (src_value, dst_value);
244 subclass_class_init (gpointer g_class,
247 GObjectClass *object_class;
249 /* e_source_camel_generate_subtype() does all the
250 * dynamic class initialization. We just do what static
251 * initialization we can here. */
253 object_class = G_OBJECT_CLASS (g_class);
254 object_class->set_property = subclass_set_property;
255 object_class->get_property = subclass_get_property;
259 subclass_instance_init (GTypeInstance *instance,
262 /* Nothing to do here, just makes a handy breakpoint. */
266 source_camel_get_property (GObject *object,
271 switch (property_id) {
275 e_source_camel_get_settings (
276 E_SOURCE_CAMEL (object)));
280 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
284 source_camel_dispose (GObject *object)
286 ESourceCamelPrivate *priv;
288 priv = E_SOURCE_CAMEL_GET_PRIVATE (object);
290 if (priv->settings != NULL) {
291 g_object_unref (priv->settings);
292 priv->settings = NULL;
295 /* Chain up to parent's dispose() method. */
296 G_OBJECT_CLASS (e_source_camel_parent_class)->dispose (object);
300 source_camel_finalize (GObject *object)
302 ESourceCamelPrivate *priv;
305 priv = E_SOURCE_CAMEL_GET_PRIVATE (object);
307 for (ii = 0; ii < priv->value_array->len; ii++)
308 g_value_unset (&g_array_index (priv->value_array, GValue, ii));
310 g_array_free (priv->value_array, TRUE);
312 /* Chain up to parent's finalize() method. */
313 G_OBJECT_CLASS (e_source_camel_parent_class)->finalize (object);
317 source_camel_constructed (GObject *object)
320 ESourceCamelClass *class;
321 ESourceCamelPrivate *priv;
322 GObjectClass *settings_class;
323 GParamSpec **properties;
324 guint ii, n_properties;
325 guint array_index = 0;
327 /* Chain up to parent's constructed() method. */
328 G_OBJECT_CLASS (e_source_camel_parent_class)->
329 constructed (object);
331 class = E_SOURCE_CAMEL_GET_CLASS (object);
332 priv = E_SOURCE_CAMEL_GET_PRIVATE (object);
334 source = e_source_extension_get_source (E_SOURCE_EXTENSION (object));
336 priv->settings = g_object_new (class->settings_type, NULL);
338 /* Here we bind all the GObject properties in the newly-created
339 * CamelSettings instance to either our own identical properties
340 * or properties in another ESourceExtensions. The bindings list
341 * at the top of the file maps out bindings to other extensions. */
343 settings_class = G_OBJECT_GET_CLASS (priv->settings);
345 properties = g_object_class_list_properties (
346 settings_class, &n_properties);
348 /* Allocate more elements than we need, since some CamelSettings
349 * properties get bound to properties of other ESourceExtensions.
350 * We'll trim off the extra elements later. */
351 g_array_set_size (priv->value_array, n_properties);
353 for (ii = 0; ii < n_properties; ii++) {
354 GParamSpec *pspec = properties[ii];
355 GBindingTransformFunc transform_to = NULL;
356 GBindingTransformFunc transform_from = NULL;
357 ESourceExtension *extension;
358 const gchar *source_property;
359 const gchar *target_property;
362 binding_index = subclass_get_binding_index (pspec);
364 /* Bind the CamelSettings property to
365 * one in a different ESourceExtension. */
366 if (binding_index >= 0) {
367 BindingData *binding;
369 binding = &bindings[binding_index];
371 extension = e_source_get_extension (
372 source, binding->extension_name);
374 source_property = binding->extension_property_name;
375 target_property = binding->settings_property_name;
377 transform_to = binding->extension_to_settings;
378 transform_from = binding->settings_to_extension;
380 /* Bind the CamelSettings property to our own
381 * equivalent E_SOURCE_PARAM_SETTING property. */
385 extension = E_SOURCE_EXTENSION (object);
387 source_property = pspec->name;
388 target_property = pspec->name;
390 /* Initialize the array element to
391 * hold the GParamSpec's value type. */
392 value = &g_array_index (
393 priv->value_array, GValue, array_index++);
394 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
396 /* Set the array element to the GParamSpec's default
397 * value. This allows us to avoid declaring our own
398 * properties with a G_PARAM_CONSTRUCT flag. */
399 g_param_value_set_default (pspec, value);
402 g_object_bind_property_full (
403 extension, source_property,
404 priv->settings, target_property,
405 G_BINDING_BIDIRECTIONAL |
406 G_BINDING_SYNC_CREATE,
407 transform_to, transform_from,
408 NULL, (GDestroyNotify) NULL);
411 /* Trim off any extra array elements. */
412 g_array_set_size (priv->value_array, array_index);
418 e_source_camel_class_init (ESourceCamelClass *class)
420 GObjectClass *object_class;
422 g_type_class_add_private (class, sizeof (ESourceCamelPrivate));
424 object_class = G_OBJECT_CLASS (class);
425 object_class->get_property = source_camel_get_property;
426 object_class->dispose = source_camel_dispose;
427 object_class->finalize = source_camel_finalize;
428 object_class->constructed = source_camel_constructed;
430 /* CamelSettings itself has no properties. */
431 class->settings_type = CAMEL_TYPE_SETTINGS;
433 /* XXX This kind of stomps on CamelSettings' namespace, but it's
434 * unlikely a CamelSettings subclass would define a property
435 * named "settings". */
436 g_object_class_install_property (
439 g_param_spec_object (
442 "The CamelSettings instance being proxied",
445 G_PARAM_STATIC_STRINGS));
449 e_source_camel_init (ESourceCamel *extension)
453 /* Zero-fill array elements when they are allocated. */
454 value_array = g_array_new (FALSE, TRUE, sizeof (GValue));
456 extension->priv = E_SOURCE_CAMEL_GET_PRIVATE (extension);
457 extension->priv->value_array = value_array;
461 * e_source_camel_register_types:
463 * Creates and registers subclasses of #ESourceCamel for each available
464 * #CamelProvider. This function should be called once during application
465 * or library initialization.
470 e_source_camel_register_types (void)
474 /* This implicitly takes care of provider initialization. */
475 list = camel_provider_list (TRUE);
477 for (link = list; link != NULL; link = g_list_next (link)) {
478 CamelProvider *provider;
481 provider = (CamelProvider *) link->data;
483 /* This is the novel part: generate and register
484 * a new ESourceCamel subclass on-the-fly for each
485 * object type listed in the provider. */
486 for (ii = 0; ii < CAMEL_NUM_PROVIDER_TYPES; ii++) {
487 CamelServiceClass *service_class = NULL;
490 service_type = provider->object_types[ii];
492 if (g_type_is_a (service_type, CAMEL_TYPE_SERVICE))
493 service_class = g_type_class_ref (service_type);
495 if (service_class != NULL) {
496 e_source_camel_generate_subtype (
498 service_class->settings_type);
499 g_type_class_unref (service_class);
508 * e_source_camel_generate_subtype:
509 * @protocol: a #CamelProvider protocol
510 * @settings_type: a subtype of #CamelSettings
512 * Generates a custom #ESourceCamel subtype for @protocol. Instances of the
513 * new subtype will contain a #CamelSettings instance of type @settings_type.
515 * This function is called as part of e_source_camel_register_types() and
516 * should not be called explicitly, except by some groupware packages that
517 * need to share package-specific settings across their mail, calendar and
518 * address book components. In that case the groupware package may choose
519 * to subclass #CamelSettings rather than #ESourceExtension since libcamel
520 * is the lowest common denominator across all components. This function
521 * provides a way for the calendar and address book components of such a
522 * package to generate an #ESourceCamel subtype for its #CamelSettings
523 * subtype without having to load all available #CamelProvider modules.
525 * Returns: the #GType of the generated #ESourceCamel subtype
530 e_source_camel_generate_subtype (const gchar *protocol,
533 ESourceCamelClass *class;
534 GObjectClass *settings_class;
535 GParamSpec **properties;
536 guint ii, n_properties;
541 const gchar *type_name;
542 const gchar *extension_name;
544 g_return_val_if_fail (protocol != NULL, G_TYPE_INVALID);
546 type_name = e_source_camel_get_type_name (protocol);
547 extension_name = e_source_camel_get_extension_name (protocol);
549 /* Check if the type name is already registered. */
550 type = g_type_from_name (type_name);
551 if (type != G_TYPE_INVALID)
554 /* The settings type must be derived from CAMEL_TYPE_SETTINGS. */
555 if (!g_type_is_a (settings_type, CAMEL_TYPE_SETTINGS)) {
557 "%s: Invalid settings type '%s' for protocol '%s'",
558 G_STRFUNC, g_type_name (settings_type), protocol);
559 return G_TYPE_INVALID;
562 memset (&type_info, 0, sizeof (GTypeInfo));
563 type_info.class_size = sizeof (ESourceCamelClass);
564 type_info.class_init = subclass_class_init;
565 type_info.instance_size = sizeof (ESourceCamel);
566 type_info.instance_init = subclass_instance_init;
568 parent_type = E_TYPE_SOURCE_CAMEL;
569 type = g_type_register_static (parent_type, type_name, &type_info, 0);
571 /* Since we have first access to the newly registered GType, and
572 * because initializing its class structure requires some of the
573 * arguments we were passed, we'll complete class initialization
574 * here rather than trying to do it all in subclass_init(). */
576 class = g_type_class_ref (type);
577 settings_class = g_type_class_ref (settings_type);
579 /* Initialize more class members. */
580 class->settings_type = G_OBJECT_CLASS_TYPE (settings_class);
581 class->parent_class.name = g_intern_string (extension_name);
583 /* For each property in the CamelSettings class, register
584 * an equivalent GObject property in this class and add an
585 * E_SOURCE_PARAM_SETTING flag so the value gets written to
586 * the ESource's key file. */
588 properties = g_object_class_list_properties (
589 settings_class, &n_properties);
591 for (ii = 0; ii < n_properties; ii++) {
594 /* Some properties in CamelSettings may be covered
595 * by other ESourceExtensions. Skip them here. */
596 if (subclass_get_binding_index (properties[ii]) >= 0)
599 pspec = param_spec_clone (properties[ii]);
600 pspec->flags |= E_SOURCE_PARAM_SETTING;
602 /* Clear the G_PARAM_CONSTRUCT flag. We apply default
603 * property values to our GValue array during instance
605 pspec->flags &= ~G_PARAM_CONSTRUCT;
607 g_object_class_install_property (
608 G_OBJECT_CLASS (class), prop_id++, pspec);
613 g_type_class_unref (class);
614 g_type_class_unref (settings_class);
620 * e_source_camel_get_settings:
621 * @extension: an #ESourceCamel
623 * Returns @extension's #ESourceCamel:settings instance, pre-configured
624 * from the #ESource to which @extension belongs. Changes to the #ESource
625 * will automatically propagate to the #ESourceCamel:settings instance and
628 * This is essentially the glue that binds #ESource to #CamelService.
629 * See e_source_camel_configure_service().
631 * Returns: a configured #CamelSettings instance
636 e_source_camel_get_settings (ESourceCamel *extension)
638 g_return_val_if_fail (E_IS_SOURCE_CAMEL (extension), NULL);
640 return extension->priv->settings;
644 * e_source_camel_get_type_name:
645 * @protocol: a #CamelProvider protocol
647 * Returns the #GType name of the registered #ESourceCamel subtype for
650 * For example, given a protocol named "imap" the function would return
651 * "ESourceCamelImap".
653 * Returns: the #ESourceCamel type name for @protocol
658 e_source_camel_get_type_name (const gchar *protocol)
662 g_return_val_if_fail (protocol != NULL, NULL);
664 buffer = g_alloca (strlen (protocol) + 16);
665 g_sprintf (buffer, "ESourceCamel%s", protocol);
666 buffer[12] = g_ascii_toupper (buffer[12]);
668 return g_intern_string (buffer);
672 * e_source_camel_get_extension_name:
673 * @protocol: a #CamelProvider protocol
675 * Returns the extension name for the #ESourceCamel subtype for @protocol.
676 * The extension name can then be passed to e_source_get_extension() to
677 * obtain an instance of the #ESourceCamel subtype.
679 * For example, given a protocol named "imap" the function would return
682 * Returns: the #ESourceCamel extension name for @protocol
687 e_source_camel_get_extension_name (const gchar *protocol)
691 g_return_val_if_fail (protocol != NULL, NULL);
693 /* Use the term "backend" for consistency with other
694 * calendar and address book backend extension names. */
695 buffer = g_alloca (strlen (protocol) + 16);
696 g_sprintf (buffer, "%s Backend", protocol);
697 buffer[0] = g_ascii_toupper (buffer[0]);
699 return g_intern_string (buffer);
703 * e_source_camel_configure_service:
704 * @source: an #ESource
705 * @service: a #CamelService
707 * This function essentially glues together @source and @serivce so their
708 * configuration settings stay synchronized. The glue itself is a shared
709 * #CamelSettings instance.
711 * Call this function immediately after creating a new #CamelService with
712 * camel_session_add_service().
717 e_source_camel_configure_service (ESource *source,
718 CamelService *service)
720 ESourceCamel *extension;
721 CamelProvider *provider;
722 CamelSettings *settings;
723 const gchar *extension_name;
725 g_return_if_fail (E_IS_SOURCE (source));
726 g_return_if_fail (CAMEL_IS_SERVICE (service));
728 provider = camel_service_get_provider (service);
729 g_return_if_fail (provider != NULL);
732 e_source_camel_get_extension_name (provider->protocol);
733 extension = e_source_get_extension (source, extension_name);
735 settings = e_source_camel_get_settings (extension);
736 camel_service_set_settings (service, settings);