1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
16 */
17
18 #include "evolution-config.h"
19
20 #include <sys/types.h>
21 #include <string.h>
22
23 #include <glib/gi18n.h>
24
25 #include <libebackend/libebackend.h>
26
27 #include "e-plugin.h"
28 #include "e-util-private.h"
29 #include "e-misc-utils.h"
30
31 /* plugin debug */
32 #define pd(x)
33 /* plugin hook debug */
34 #define phd(x)
35
36 /*
37 * <camel-plugin
38 * class="org.gnome.camel.plugin.provider:1.0"
39 * id="org.gnome.camel.provider.imap:1.0"
40 * type="shlib"
41 * location="/opt/gnome2/lib/camel/1.0/libcamelimap.so"
42 * factory="camel_imap_provider_new">
43 * <name>imap</name>
44 * <description>IMAP4 and IMAP4v1 mail store</description>
45 * <class-data class="org.gnome.camel.plugin.provider:1.0"
46 * protocol="imap"
47 * domain="mail"
48 * flags="remote,source,storage,ssl"/>
49 * </camel-plugin>
50 *
51 * <camel-plugin
52 * class="org.gnome.camel.plugin.sasl:1.0"
53 * id="org.gnome.camel.sasl.plain:1.0"
54 * type="shlib"
55 * location="/opt/gnome2/lib/camel/1.0/libcamelsasl.so"
56 * factory="camel_sasl_plain_new">
57 * <name>PLAIN</name>
58 * <description>SASL PLAIN authentication mechanism</description>
59 * </camel-plugin>
60 */
61
62 /* EPlugin stuff */
63
64 /* global table of plugin types by pluginclass.type */
65 static GHashTable *ep_types;
66 /* global table of plugins by plugin.id */
67 static GHashTable *ep_plugins;
68 /* the list of disabled plugins from GSettings */
69 static GSList *ep_disabled;
70
71 /* All classes which implement EPluginHooks, by class.id */
72 static GHashTable *eph_types;
73
74 struct _plugin_doc {
75 struct _plugin_doc *next;
76 struct _plugin_doc *prev;
77
78 gchar *filename;
79 xmlDocPtr doc;
80 };
81
82 enum {
83 EP_PROP_0,
84 EP_PROP_ENABLED
85 };
86
G_DEFINE_TYPE(EPlugin,e_plugin,G_TYPE_OBJECT)87 G_DEFINE_TYPE (
88 EPlugin,
89 e_plugin,
90 G_TYPE_OBJECT)
91
92 static gboolean
93 ep_check_enabled (const gchar *id)
94 {
95 /* Return TRUE if 'id' is NOT in the disabled list. */
96 return !g_slist_find_custom (ep_disabled, id, (GCompareFunc) strcmp);
97 }
98
99 static void
ep_set_enabled(const gchar * id,gint state)100 ep_set_enabled (const gchar *id,
101 gint state)
102 {
103 GSettings *settings;
104 GSList *link;
105 GPtrArray *array;
106
107 /* Bail out if no change to state, when expressed as a boolean: */
108 if ((state == 0) == (ep_check_enabled (id) == 0))
109 return;
110
111 if (state) {
112 GSList *link;
113
114 link = g_slist_find_custom (
115 ep_disabled, id, (GCompareFunc) strcmp);
116 if (link != NULL) {
117 g_free (link->data);
118 ep_disabled = g_slist_remove_link (ep_disabled, link);
119 }
120 } else
121 ep_disabled = g_slist_prepend (ep_disabled, g_strdup (id));
122
123 settings = e_util_ref_settings ("org.gnome.evolution");
124 array = g_ptr_array_new ();
125 for (link = ep_disabled; link != NULL; link = link->next)
126 g_ptr_array_add (array, link->data);
127 g_ptr_array_add (array, NULL);
128 g_settings_set_strv (
129 settings, "disabled-eplugins",
130 (const gchar * const *) array->pdata);
131 g_ptr_array_free (array, TRUE);
132 g_object_unref (settings);
133 }
134
135 static gint
ep_construct(EPlugin * ep,xmlNodePtr root)136 ep_construct (EPlugin *ep,
137 xmlNodePtr root)
138 {
139 xmlNodePtr node;
140 gint res = -1;
141 gchar *localedir;
142
143 ep->domain = e_plugin_xml_prop (root, "domain");
144 if (ep->domain
145 && (localedir = e_plugin_xml_prop (root, "localedir"))) {
146 #ifdef G_OS_WIN32
147 gchar *mapped_localedir =
148 e_util_replace_prefix (
149 EVOLUTION_PREFIX,
150 e_util_get_prefix (),
151 localedir);
152 g_free (localedir);
153 localedir = mapped_localedir;
154 #endif
155 bindtextdomain (ep->domain, localedir);
156 g_free (localedir);
157 }
158
159 ep->name = e_plugin_xml_prop_domain (root, "name", ep->domain);
160
161 node = root->children;
162 while (node) {
163 if (strcmp ((gchar *) node->name, "hook") == 0) {
164 EPluginHook *hook;
165 EPluginHookClass *type;
166 gchar *class = e_plugin_xml_prop (node, "class");
167
168 if (class == NULL) {
169 g_warning (
170 "Plugin '%s' load failed in '%s', "
171 "missing class property for hook",
172 ep->id, ep->path);
173 goto fail;
174 }
175
176 if (ep->enabled
177 && eph_types != NULL
178 && (type = g_hash_table_lookup (
179 eph_types, class)) != NULL) {
180 g_free (class);
181 hook = g_object_new (G_OBJECT_CLASS_TYPE (type), NULL);
182 res = type->construct (hook, ep, node);
183 if (res == -1) {
184 g_warning (
185 "Plugin '%s' failed to "
186 "load hook", ep->name);
187 g_object_unref (hook);
188 goto fail;
189 } else {
190 ep->hooks = g_slist_append (ep->hooks, hook);
191 }
192 } else {
193 g_free (class);
194 }
195 } else if (strcmp ((gchar *) node->name, "description") == 0) {
196 ep->description =
197 e_plugin_xml_content_domain (node, ep->domain);
198 } else if (strcmp ((gchar *) node->name, "author") == 0) {
199 gchar *name = e_plugin_xml_prop (node, "name");
200 gchar *email = e_plugin_xml_prop (node, "email");
201
202 if (name || email) {
203 EPluginAuthor *epa = g_malloc0 (sizeof (*epa));
204
205 epa->name = name;
206 epa->email = email;
207 ep->authors = g_slist_append (ep->authors, epa);
208 }
209 }
210 node = node->next;
211 }
212 res = 0;
213 fail:
214 return res;
215 }
216
217 static void
ep_enable(EPlugin * ep,gint state)218 ep_enable (EPlugin *ep,
219 gint state)
220 {
221 GSList *iter;
222
223 ep->enabled = state;
224 for (iter = ep->hooks; iter != NULL; iter = iter->next) {
225 EPluginHook *hook = iter->data;
226 e_plugin_hook_enable (hook, state);
227 }
228
229 ep_set_enabled (ep->id, state);
230 }
231
232 static void
ep_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)233 ep_set_property (GObject *object,
234 guint property_id,
235 const GValue *value,
236 GParamSpec *pspec)
237 {
238 switch (property_id) {
239 case EP_PROP_ENABLED:
240 e_plugin_enable (
241 E_PLUGIN (object),
242 g_value_get_boolean (value));
243 return;
244 }
245
246 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
247 }
248
249 static void
ep_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)250 ep_get_property (GObject *object,
251 guint property_id,
252 GValue *value,
253 GParamSpec *pspec)
254 {
255 EPlugin *ep = E_PLUGIN (object);
256
257 switch (property_id) {
258 case EP_PROP_ENABLED:
259 g_value_set_boolean (value, ep->enabled);
260 return;
261 }
262
263 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
264 }
265
266 static void
ep_finalize(GObject * object)267 ep_finalize (GObject *object)
268 {
269 EPlugin *ep = E_PLUGIN (object);
270
271 g_free (ep->id);
272 g_free (ep->description);
273 g_free (ep->name);
274 g_free (ep->domain);
275
276 g_slist_foreach (ep->hooks, (GFunc) g_object_unref, NULL);
277 g_slist_free (ep->hooks);
278
279 /* Chain up to parent's finalize() method. */
280 G_OBJECT_CLASS (e_plugin_parent_class)->finalize (object);
281 }
282
283 static void
e_plugin_class_init(EPluginClass * class)284 e_plugin_class_init (EPluginClass *class)
285 {
286 GObjectClass *object_class;
287
288 object_class = G_OBJECT_CLASS (class);
289 object_class->set_property = ep_set_property;
290 object_class->get_property = ep_get_property;
291 object_class->finalize = ep_finalize;
292
293 class->construct = ep_construct;
294 class->enable = ep_enable;
295
296 g_object_class_install_property (
297 object_class,
298 EP_PROP_ENABLED,
299 g_param_spec_boolean (
300 "enabled",
301 "Enabled",
302 "Whether the plugin is enabled",
303 TRUE,
304 G_PARAM_READWRITE));
305 }
306
307 static void
e_plugin_init(EPlugin * ep)308 e_plugin_init (EPlugin *ep)
309 {
310 ep->enabled = TRUE;
311 }
312
313 static EPlugin *
ep_load_plugin(xmlNodePtr root,struct _plugin_doc * pdoc)314 ep_load_plugin (xmlNodePtr root,
315 struct _plugin_doc *pdoc)
316 {
317 gchar *prop, *id;
318 EPluginClass *class;
319 EPlugin *ep;
320
321 id = e_plugin_xml_prop (root, "id");
322 if (id == NULL) {
323 g_warning ("Invalid e-plugin entry in '%s': no id", pdoc->filename);
324 return NULL;
325 }
326
327 if (g_hash_table_lookup (ep_plugins, id)) {
328 g_warning ("Plugin '%s' already defined", id);
329 g_free (id);
330 return NULL;
331 }
332
333 prop = (gchar *) xmlGetProp (root, (const guchar *)"type");
334 if (prop == NULL) {
335 g_free (id);
336 g_warning ("Invalid e-plugin entry in '%s': no type", pdoc->filename);
337 return NULL;
338 }
339
340 /* If we can't find a plugin, add it to a pending list
341 * which is checked when a new type is registered. */
342 class = g_hash_table_lookup (ep_types, prop);
343 if (class == NULL) {
344 g_free (id);
345 xmlFree (prop);
346 return NULL;
347 }
348 xmlFree (prop);
349
350 ep = g_object_new (G_TYPE_FROM_CLASS (class), NULL);
351 ep->id = id;
352 ep->path = g_strdup (pdoc->filename);
353 ep->enabled = ep_check_enabled (id);
354 if (e_plugin_construct (ep, root) == -1)
355 e_plugin_enable (ep, FALSE);
356 g_hash_table_insert (ep_plugins, ep->id, ep);
357
358 return ep;
359 }
360
361 static gint
ep_load(const gchar * filename,gint load_level)362 ep_load (const gchar *filename,
363 gint load_level)
364 {
365 xmlDocPtr doc;
366 xmlNodePtr root;
367 EPlugin *ep = NULL;
368 struct _plugin_doc *pdoc;
369
370 doc = e_xml_parse_file (filename);
371 if (doc == NULL)
372 return -1;
373
374 root = xmlDocGetRootElement (doc);
375 if (strcmp ((gchar *) root->name, "e-plugin-list") != 0) {
376 g_warning ("No <e-plugin-list> root element: %s", filename);
377 xmlFreeDoc (doc);
378 return -1;
379 }
380
381 pdoc = g_malloc0 (sizeof (*pdoc));
382 pdoc->doc = doc;
383 pdoc->filename = g_strdup (filename);
384
385 for (root = root->children; root; root = root->next) {
386 if (strcmp ((gchar *) root->name, "e-plugin") == 0) {
387 gchar *plugin_load_level, *is_system_plugin;
388
389 plugin_load_level = NULL;
390 plugin_load_level = e_plugin_xml_prop (root, "load_level");
391 if (plugin_load_level) {
392 if ((atoi (plugin_load_level) == load_level)) {
393 ep = ep_load_plugin (root, pdoc);
394
395 if (ep && load_level == 1)
396 e_plugin_invoke (
397 ep, "load_plugin_type_register_function", NULL);
398 }
399 } else if (load_level == 2) {
400 ep = ep_load_plugin (root, pdoc);
401 }
402
403 if (ep) {
404 /* README: Maybe we can use load_levels to
405 * achieve the same thing. But it may be
406 * confusing for a plugin writer. */
407 is_system_plugin =
408 e_plugin_xml_prop (root, "system_plugin");
409 if (g_strcmp0 (is_system_plugin, "true") == 0) {
410 e_plugin_enable (ep, TRUE);
411 ep->flags |= E_PLUGIN_FLAGS_SYSTEM_PLUGIN;
412 } else
413 ep->flags &= ~E_PLUGIN_FLAGS_SYSTEM_PLUGIN;
414 g_free (is_system_plugin);
415
416 ep = NULL;
417 }
418 }
419 }
420
421 xmlFreeDoc (pdoc->doc);
422 g_free (pdoc->filename);
423 g_free (pdoc);
424
425 return 0;
426 }
427
428 static void
plugin_load_subclass(GType type,GHashTable * hash_table)429 plugin_load_subclass (GType type,
430 GHashTable *hash_table)
431 {
432 EPluginClass *class;
433
434 class = g_type_class_ref (type);
435 g_hash_table_insert (hash_table, (gpointer) class->type, class);
436 }
437
438 static void
plugin_hook_load_subclass(GType type,GHashTable * hash_table)439 plugin_hook_load_subclass (GType type,
440 GHashTable *hash_table)
441 {
442 EPluginHookClass *hook_class;
443 EPluginHookClass *dupe_class;
444 gpointer key;
445
446 hook_class = g_type_class_ref (type);
447
448 /* Sanity check the hook class. */
449 if (hook_class->id == NULL || *hook_class->id == '\0') {
450 g_warning (
451 "%s has no hook ID, so skipping",
452 G_OBJECT_CLASS_NAME (hook_class));
453 g_type_class_unref (hook_class);
454 return;
455 }
456
457 /* Check for class ID collisions. */
458 dupe_class = g_hash_table_lookup (hash_table, hook_class->id);
459 if (dupe_class != NULL) {
460 g_warning (
461 "%s and %s have the same hook "
462 "ID ('%s'), so skipping %s",
463 G_OBJECT_CLASS_NAME (dupe_class),
464 G_OBJECT_CLASS_NAME (hook_class),
465 hook_class->id,
466 G_OBJECT_CLASS_NAME (hook_class));
467 g_type_class_unref (hook_class);
468 return;
469 }
470
471 key = (gpointer) hook_class->id;
472 g_hash_table_insert (hash_table, key, hook_class);
473 }
474
475 static void
e_plugin_traverse_directory(const gchar * dirname,gint index)476 e_plugin_traverse_directory (const gchar *dirname,
477 gint index)
478 {
479 GDir *dir;
480 const gchar *d;
481
482 pd (printf ("scanning plugin dir '%s'\n", dirname));
483
484 dir = g_dir_open (dirname, 0, NULL);
485
486 if (!dir)
487 return;
488
489 while ((d = g_dir_read_name (dir))) {
490 if (g_str_has_suffix (d, ".eplug")) {
491 gchar *name;
492
493 name = g_build_filename (dirname, d, NULL);
494 ep_load (name, index);
495 g_free (name);
496 }
497 }
498
499 g_dir_close (dir);
500 }
501
502 /**
503 * e_plugin_load_plugins:
504 *
505 * Scan the search path, looking for plugin definitions, and load them
506 * into memory.
507 *
508 * Return value: Returns -1 if an error occurred.
509 **/
510 gint
e_plugin_load_plugins(void)511 e_plugin_load_plugins (void)
512 {
513 GSettings *settings;
514 GPtrArray *variants;
515 gchar **strv;
516 gint i;
517
518 if (eph_types != NULL)
519 return 0;
520
521 ep_types = g_hash_table_new (g_str_hash, g_str_equal);
522 eph_types = g_hash_table_new (g_str_hash, g_str_equal);
523 ep_plugins = g_hash_table_new (g_str_hash, g_str_equal);
524
525 /* We require that all GTypes for EPlugin and EPluginHook
526 * subclasses be registered prior to loading any plugins.
527 * It greatly simplifies the loading process. */
528 e_type_traverse (
529 E_TYPE_PLUGIN, (ETypeFunc)
530 plugin_load_subclass, ep_types);
531 e_type_traverse (
532 E_TYPE_PLUGIN_HOOK, (ETypeFunc)
533 plugin_hook_load_subclass, eph_types);
534
535 settings = e_util_ref_settings ("org.gnome.evolution");
536 strv = g_settings_get_strv (settings, "disabled-eplugins");
537 for (i = 0, ep_disabled = NULL; strv[i] != NULL; i++)
538 ep_disabled = g_slist_append (ep_disabled, g_strdup (strv[i]));
539 g_strfreev (strv);
540 g_object_unref (settings);
541
542 variants = e_util_get_directory_variants (EVOLUTION_PLUGINDIR, EVOLUTION_PREFIX, TRUE);
543
544 for (i = 0; i < 3; i++) {
545 if (variants) {
546 guint jj;
547
548 for (jj = 0; jj < variants->len; jj++) {
549 const gchar *dirname = g_ptr_array_index (variants, jj);
550
551 if (dirname && *dirname)
552 e_plugin_traverse_directory (dirname, i);
553 }
554 } else {
555 e_plugin_traverse_directory (EVOLUTION_PLUGINDIR, i);
556 }
557 }
558
559 if (variants)
560 g_ptr_array_unref (variants);
561
562 return 0;
563 }
564
565 static void
ep_list_plugin(gpointer key,gpointer val,gpointer dat)566 ep_list_plugin (gpointer key,
567 gpointer val,
568 gpointer dat)
569 {
570 GSList **l = (GSList **) dat;
571
572 *l = g_slist_prepend(*l, g_object_ref(val));
573 }
574
575 /**
576 * e_plugin_list_plugins: List all plugins.
577 *
578 * Static class method to retrieve a list of all current plugins. They
579 * are listed in no particular order.
580 *
581 * Return value: A GSList of all plugins, they must be
582 * g_object_unref'd and the list freed.
583 **/
584 GSList *
e_plugin_list_plugins(void)585 e_plugin_list_plugins (void)
586 {
587 GSList *l = NULL;
588
589 if (ep_plugins)
590 g_hash_table_foreach (ep_plugins, ep_list_plugin, &l);
591
592 return l;
593 }
594
595 /**
596 * e_plugin_construct:
597 * @plugin: an #EPlugin
598 * @root: The XML root node of the sub-tree containing the plugin
599 * definition.
600 *
601 * Helper to invoke the construct virtual method.
602 *
603 * Return value: The return from the construct virtual method.
604 **/
605 gint
e_plugin_construct(EPlugin * plugin,xmlNodePtr root)606 e_plugin_construct (EPlugin *plugin,
607 xmlNodePtr root)
608 {
609 EPluginClass *class;
610
611 g_return_val_if_fail (E_IS_PLUGIN (plugin), -1);
612
613 class = E_PLUGIN_GET_CLASS (plugin);
614 g_return_val_if_fail (class != NULL, -1);
615 g_return_val_if_fail (class->construct != NULL, -1);
616
617 return class->construct (plugin, root);
618 }
619
620 /**
621 * e_plugin_invoke:
622 * @plugin: an #EPlugin
623 * @name: The name of the function to invoke. The format of this name
624 * will depend on the EPlugin type and its language conventions.
625 * @data: The argument to the function. Its actual type depends on
626 * the hook on which the function resides. It is up to the called
627 * function to get this right.
628 *
629 * Helper to invoke the invoke virtual method.
630 *
631 * Return value: The return of the plugin invocation.
632 **/
633 gpointer
e_plugin_invoke(EPlugin * plugin,const gchar * name,gpointer data)634 e_plugin_invoke (EPlugin *plugin,
635 const gchar *name,
636 gpointer data)
637 {
638 EPluginClass *class;
639
640 g_return_val_if_fail (E_IS_PLUGIN (plugin), NULL);
641 g_return_val_if_fail (name != NULL, NULL);
642
643 /* Prevent invocation on a disabled plugin. */
644 g_return_val_if_fail (plugin->enabled, NULL);
645
646 class = E_PLUGIN_GET_CLASS (plugin);
647 g_return_val_if_fail (class != NULL, NULL);
648 g_return_val_if_fail (class->invoke != NULL, NULL);
649
650 return class->invoke (plugin, name, data);
651 }
652
653 /**
654 * e_plugin_get_symbol:
655 * @plugin: an #EPlugin
656 * @name: The name of the symbol to fetch. The format of this name
657 * will depend on the EPlugin type and its language conventions.
658 *
659 * Helper to fetch a symbol name from a plugin.
660 *
661 * Return value: the symbol value, or %NULL if not found
662 **/
663 gpointer
e_plugin_get_symbol(EPlugin * plugin,const gchar * name)664 e_plugin_get_symbol (EPlugin *plugin,
665 const gchar *name)
666 {
667 EPluginClass *class;
668
669 g_return_val_if_fail (E_IS_PLUGIN (plugin), NULL);
670
671 class = E_PLUGIN_GET_CLASS (plugin);
672 g_return_val_if_fail (class != NULL, NULL);
673 g_return_val_if_fail (class->get_symbol != NULL, NULL);
674
675 return class->get_symbol (plugin, name);
676 }
677
678 /**
679 * e_plugin_enable:
680 * @plugin: an #EPlugin
681 * @state: %TRUE to enable, %FALSE to disable
682 *
683 * Set the enable state of a plugin.
684 *
685 * THIS IS NOT FULLY IMPLEMENTED YET
686 **/
687 void
e_plugin_enable(EPlugin * plugin,gint state)688 e_plugin_enable (EPlugin *plugin,
689 gint state)
690 {
691 EPluginClass *class;
692
693 g_return_if_fail (E_IS_PLUGIN (plugin));
694
695 if ((plugin->enabled == 0) == (state == 0))
696 return;
697
698 class = E_PLUGIN_GET_CLASS (plugin);
699 g_return_if_fail (class != NULL);
700 g_return_if_fail (class->enable != NULL);
701
702 class->enable (plugin, state);
703
704 g_object_notify (G_OBJECT (plugin), "enabled");
705 }
706
707 /**
708 * e_plugin_get_configure_widget
709 * @plugin: an #EPlugin
710 *
711 * Plugin itself should have implemented "e_plugin_lib_get_configure_widget"
712 * function * of prototype EPluginLibGetConfigureWidgetFunc.
713 *
714 * Returns: Configure widget or %NULL
715 **/
716 GtkWidget *
e_plugin_get_configure_widget(EPlugin * plugin)717 e_plugin_get_configure_widget (EPlugin *plugin)
718 {
719 EPluginClass *class;
720
721 g_return_val_if_fail (E_IS_PLUGIN (plugin), NULL);
722
723 class = E_PLUGIN_GET_CLASS (plugin);
724 g_return_val_if_fail (class != NULL, NULL);
725
726 if (class->get_configure_widget == NULL)
727 return NULL;
728
729 return class->get_configure_widget (plugin);
730 }
731
732 /**
733 * e_plugin_xml_prop:
734 * @node: An XML node.
735 * @id: The name of the property to retrieve.
736 *
737 * A static helper function to look up a property on an XML node, and
738 * ensure it is allocated in GLib system memory.
739 *
740 * Return value: The property, allocated in GLib memory, or NULL if no
741 * such property exists.
742 **/
743 gchar *
e_plugin_xml_prop(xmlNodePtr node,const gchar * id)744 e_plugin_xml_prop (xmlNodePtr node,
745 const gchar *id)
746 {
747 xmlChar *xml_prop;
748 gchar *glib_prop = NULL;
749
750 xml_prop = xmlGetProp (node, (xmlChar *) id);
751
752 if (xml_prop != NULL) {
753 glib_prop = g_strdup ((gchar *) xml_prop);
754 xmlFree (xml_prop);
755 }
756
757 return glib_prop;
758 }
759
760 /**
761 * e_plugin_xml_prop_domain:
762 * @node: An XML node.
763 * @id: The name of the property to retrieve.
764 * @domain: The translation domain for this string.
765 *
766 * A static helper function to look up a property on an XML node, and
767 * translate it based on @domain.
768 *
769 * Return value: The property, allocated in GLib memory, or NULL if no
770 * such property exists.
771 **/
772 gchar *
e_plugin_xml_prop_domain(xmlNodePtr node,const gchar * id,const gchar * domain)773 e_plugin_xml_prop_domain (xmlNodePtr node,
774 const gchar *id,
775 const gchar *domain)
776 {
777 gchar *p, *out;
778
779 p = (gchar *) xmlGetProp (node, (const guchar *) id);
780 if (p == NULL)
781 return NULL;
782
783 out = g_strdup (dgettext (domain, p));
784 xmlFree (p);
785
786 return out;
787 }
788
789 /**
790 * e_plugin_xml_int:
791 * @node: An XML node.
792 * @id: The name of the property to retrieve.
793 * @def: A default value if the property doesn't exist. Can be used
794 * to determine if the property isn't set.
795 *
796 * A static helper function to look up a property on an XML node as an
797 * integer. If the property doesn't exist, then @def is returned as a
798 * default value instead.
799 *
800 * Return value: The value if set, or @def if not.
801 **/
802 gint
e_plugin_xml_int(xmlNodePtr node,const gchar * id,gint def)803 e_plugin_xml_int (xmlNodePtr node,
804 const gchar *id,
805 gint def)
806 {
807 gchar *p = (gchar *) xmlGetProp (node, (const guchar *) id);
808
809 if (p)
810 return atoi (p);
811 else
812 return def;
813 }
814
815 /**
816 * e_plugin_xml_content:
817 * @node:
818 *
819 * A static helper function to retrieve the entire textual content of
820 * an XML node, and ensure it is allocated in GLib system memory. If
821 * GLib isn't using the system malloc them it must copy the content.
822 *
823 * Return value: The node content, allocated in GLib memory.
824 **/
825 gchar *
e_plugin_xml_content(xmlNodePtr node)826 e_plugin_xml_content (xmlNodePtr node)
827 {
828 gchar *p = (gchar *) xmlNodeGetContent (node);
829
830 if (g_mem_is_system_malloc ()) {
831 return p;
832 } else {
833 gchar * out = g_strdup (p);
834
835 if (p)
836 xmlFree (p);
837 return out;
838 }
839 }
840
841 /**
842 * e_plugin_xml_content_domain:
843 * @node:
844 * @domain:
845 *
846 * A static helper function to retrieve the entire textual content of
847 * an XML node, and ensure it is allocated in GLib system memory. If
848 * GLib isn't using the system malloc them it must copy the content.
849 *
850 * Return value: The node content, allocated in GLib memory.
851 **/
852 gchar *
e_plugin_xml_content_domain(xmlNodePtr node,const gchar * domain)853 e_plugin_xml_content_domain (xmlNodePtr node,
854 const gchar *domain)
855 {
856 gchar *p, *out;
857
858 p = (gchar *) xmlNodeGetContent (node);
859 if (p == NULL)
860 return NULL;
861
862 out = g_strdup (dgettext (domain, p));
863 xmlFree (p);
864
865 return out;
866 }
867
868 /* ********************************************************************** */
869
G_DEFINE_TYPE(EPluginHook,e_plugin_hook,G_TYPE_OBJECT)870 G_DEFINE_TYPE (
871 EPluginHook,
872 e_plugin_hook,
873 G_TYPE_OBJECT)
874
875 static gint
876 plugin_hook_construct (EPluginHook *plugin_hook,
877 EPlugin *plugin,
878 xmlNodePtr root)
879 {
880 plugin_hook->plugin = plugin;
881
882 return 0;
883 }
884
885 static void
plugin_hook_enable(EPluginHook * plugin_hook,gint state)886 plugin_hook_enable (EPluginHook *plugin_hook,
887 gint state)
888 {
889 /* NOOP */
890 }
891
892 static void
e_plugin_hook_class_init(EPluginHookClass * class)893 e_plugin_hook_class_init (EPluginHookClass *class)
894 {
895 class->construct = plugin_hook_construct;
896 class->enable = plugin_hook_enable;
897 }
898
899 static void
e_plugin_hook_init(EPluginHook * hook)900 e_plugin_hook_init (EPluginHook *hook)
901 {
902 }
903
904 /**
905 * e_plugin_hook_enable:
906 * @plugin_hook:
907 * @state:
908 *
909 * Set the enabled state of the plugin hook. This is called by the
910 * plugin code.
911 *
912 * THIS IS NOT FULY IMEPLEMENTED YET
913 **/
914 void
e_plugin_hook_enable(EPluginHook * plugin_hook,gint state)915 e_plugin_hook_enable (EPluginHook *plugin_hook,
916 gint state)
917 {
918 EPluginHookClass *class;
919
920 g_return_if_fail (E_IS_PLUGIN_HOOK (plugin_hook));
921
922 class = E_PLUGIN_HOOK_GET_CLASS (plugin_hook);
923 g_return_if_fail (class != NULL);
924 g_return_if_fail (class->enable != NULL);
925
926 class->enable (plugin_hook, state);
927 }
928
929 /**
930 * e_plugin_hook_mask:
931 * @root: An XML node.
932 * @map: A zero-fill terminated array of EPluginHookTargeKeys used to
933 * map a string with a bit value.
934 * @prop: The property name.
935 *
936 * This is a static helper function which looks up a property @prop on
937 * the XML node @root, and then uses the @map table to convert it into
938 * a bitmask. The property value is a comma separated list of
939 * enumeration strings which are indexed into the @map table.
940 *
941 * Return value: A bitmask representing the inclusive-or of all of the
942 * integer values of the corresponding string id's stored in the @map.
943 **/
944 guint32
e_plugin_hook_mask(xmlNodePtr root,const EPluginHookTargetKey * map,const gchar * prop)945 e_plugin_hook_mask (xmlNodePtr root,
946 const EPluginHookTargetKey *map,
947 const gchar *prop)
948 {
949 gchar *val, *p, *start, c;
950 guint32 mask = 0;
951
952 val = (gchar *) xmlGetProp (root, (const guchar *) prop);
953 if (val == NULL)
954 return 0;
955
956 p = val;
957 do {
958 start = p;
959 while (*p && *p != ',')
960 p++;
961 c = *p;
962 *p = 0;
963 if (start != p) {
964 gint i;
965
966 for (i = 0; map[i].key; i++) {
967 if (!strcmp (map[i].key, start)) {
968 mask |= map[i].value;
969 break;
970 }
971 }
972 }
973 *p++ = c;
974 } while (c);
975
976 xmlFree (val);
977
978 return mask;
979 }
980
981 /**
982 * e_plugin_hook_id:
983 * @root:
984 * @map:
985 * @prop:
986 *
987 * This is a static helper function which looks up a property @prop on
988 * the XML node @root, and then uses the @map table to convert it into
989 * an integer.
990 *
991 * This is used as a helper wherever you need to represent an
992 * enumerated value in the XML.
993 *
994 * Return value: If the @prop value is in @map, then the corresponding
995 * integer value, if not, then ~0.
996 **/
997 guint32
e_plugin_hook_id(xmlNodePtr root,const EPluginHookTargetKey * map,const gchar * prop)998 e_plugin_hook_id (xmlNodePtr root,
999 const EPluginHookTargetKey *map,
1000 const gchar *prop)
1001 {
1002 gchar *val;
1003 gint i;
1004
1005 val = (gchar *) xmlGetProp (root, (const guchar *) prop);
1006 if (val == NULL)
1007 return ~0;
1008
1009 for (i = 0; map[i].key; i++) {
1010 if (!strcmp (map[i].key, val)) {
1011 xmlFree (val);
1012 return map[i].value;
1013 }
1014 }
1015
1016 xmlFree (val);
1017
1018 return ~0;
1019 }
1020