evolution-3.6.4/e-util/e-plugin.c

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 }