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