libpurple/plugins.c

changeset 40963
f2abd04191b7
parent 40945
bbeb2e98ea5b
child 40978
2cb285cacbfd
equal deleted inserted replaced
40962:19e53c9cab73 40963:f2abd04191b7
29 #include "enums.h" 29 #include "enums.h"
30 #include "plugins.h" 30 #include "plugins.h"
31 #include "signals.h" 31 #include "signals.h"
32 #include "util.h" 32 #include "util.h"
33 33
34 typedef struct _PurplePluginInfoPrivate PurplePluginInfoPrivate;
35
36 /**************************************************************************
37 * Plugin info private data
38 **************************************************************************/
39 struct _PurplePluginInfoPrivate {
40 char *ui_requirement; /* ID of UI that is required to load the plugin */
41 char *error; /* Why a plugin is not loadable */
42
43 PurplePluginInfoFlags flags; /* Flags for the plugin */
44
45 /* Callback that returns a list of actions the plugin can perform */
46 PurplePluginActionsCb actions_cb;
47
48 /* Callback that returns extra information about a plugin */
49 PurplePluginExtraCb extra_cb;
50
51 /* Callback that returns a preferences frame for a plugin */
52 PurplePluginPrefFrameCb pref_frame_cb;
53
54 /* Callback that returns a preferences request handle for a plugin */
55 PurplePluginPrefRequestCb pref_request_cb;
56
57 /* TRUE if a plugin has been unloaded at least once. Auto-load
58 * plugins that have been unloaded once will not be auto-loaded again. */
59 gboolean unloaded;
60 };
61
62 enum
63 {
64 PROP_0,
65 PROP_UI_REQUIREMENT,
66 PROP_ACTIONS_CB,
67 PROP_EXTRA_CB,
68 PROP_PREF_FRAME_CB,
69 PROP_PREF_REQUEST_CB,
70 PROP_FLAGS,
71 PROP_LAST
72 };
73
74 G_DEFINE_TYPE_WITH_PRIVATE(PurplePluginInfo, purple_plugin_info,
75 GPLUGIN_TYPE_PLUGIN_INFO);
76
77 /************************************************************************** 34 /**************************************************************************
78 * Globals 35 * Globals
79 **************************************************************************/ 36 **************************************************************************/
80 static GList *loaded_plugins = NULL; 37 static GList *loaded_plugins = NULL;
81 static GList *plugins_to_disable = NULL; 38 static GList *plugins_to_disable = NULL;
86 static gboolean 43 static gboolean
87 plugin_loading_cb(GObject *manager, PurplePlugin *plugin, GError **error, 44 plugin_loading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
88 gpointer data) 45 gpointer data)
89 { 46 {
90 PurplePluginInfo *info; 47 PurplePluginInfo *info;
91 PurplePluginInfoPrivate *priv; 48 const gchar *info_error = NULL;
92 49
93 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE); 50 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
94 51
95 info = purple_plugin_get_info(plugin); 52 info = purple_plugin_get_info(plugin);
96 if (!info) 53 if (!info)
97 return TRUE; /* a GPlugin internal plugin */ 54 return TRUE; /* a GPlugin internal plugin */
98 55
99 priv = purple_plugin_info_get_instance_private(info); 56 info_error = purple_plugin_info_get_error(info);
100 57 if(info_error != NULL) {
101 if (priv->error) {
102 gchar *filename = gplugin_plugin_get_filename(plugin); 58 gchar *filename = gplugin_plugin_get_filename(plugin);
103 purple_debug_error("plugins", "Failed to load plugin %s: %s", 59 purple_debug_error("plugins", "Failed to load plugin %s: %s",
104 filename, 60 filename,
105 priv->error); 61 info_error);
106 62
107 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, 63 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
108 "Plugin is not loadable: %s", priv->error); 64 "Plugin is not loadable: %s", info_error);
109 65
110 g_free(filename); 66 g_free(filename);
111 return FALSE; 67 return FALSE;
112 } 68 }
113 69
156 112
157 static void 113 static void
158 plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin) 114 plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin)
159 { 115 {
160 PurplePluginInfo *info; 116 PurplePluginInfo *info;
161 PurplePluginInfoPrivate *priv;
162 117
163 g_return_if_fail(PURPLE_IS_PLUGIN(plugin)); 118 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
164 119
165 info = purple_plugin_get_info(plugin); 120 info = purple_plugin_get_info(plugin);
166 if (!info) 121 if (!info)
167 return; /* a GPlugin internal plugin */ 122 return; /* a GPlugin internal plugin */
168 123
169 priv = purple_plugin_info_get_instance_private(info);
170
171 /* cancel any pending dialogs the plugin has */ 124 /* cancel any pending dialogs the plugin has */
172 purple_request_close_with_handle(plugin); 125 purple_request_close_with_handle(plugin);
173 purple_notify_close_with_handle(plugin); 126 purple_notify_close_with_handle(plugin);
174 127
175 purple_signals_disconnect_by_handle(plugin); 128 purple_signals_disconnect_by_handle(plugin);
176 purple_signals_unregister_by_instance(plugin); 129 purple_signals_unregister_by_instance(plugin);
177 130
178 priv->unloaded = TRUE; 131 purple_plugin_info_set_unloaded(info, TRUE);
179 132
180 loaded_plugins = g_list_remove(loaded_plugins, plugin); 133 loaded_plugins = g_list_remove(loaded_plugins, plugin);
181 plugins_to_disable = g_list_remove(plugins_to_disable, plugin); 134 plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
182 135
183 purple_prefs_disconnect_by_handle(plugin); 136 purple_prefs_disconnect_by_handle(plugin);
297 #warning TODO: Implement this when GPlugin can return dependent plugins. 250 #warning TODO: Implement this when GPlugin can return dependent plugins.
298 return NULL; 251 return NULL;
299 } 252 }
300 253
301 /************************************************************************** 254 /**************************************************************************
302 * GObject code for PurplePluginInfo
303 **************************************************************************/
304 /* GObject initialization function */
305 static void
306 purple_plugin_info_init(PurplePluginInfo *info)
307 {
308 }
309
310 /* Set method for GObject properties */
311 static void
312 purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
313 GParamSpec *pspec)
314 {
315 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
316 PurplePluginInfoPrivate *priv =
317 purple_plugin_info_get_instance_private(info);
318
319 switch (param_id) {
320 case PROP_UI_REQUIREMENT:
321 priv->ui_requirement = g_value_dup_string(value);
322 break;
323 case PROP_ACTIONS_CB:
324 priv->actions_cb = g_value_get_pointer(value);
325 break;
326 case PROP_EXTRA_CB:
327 priv->extra_cb = g_value_get_pointer(value);
328 break;
329 case PROP_PREF_FRAME_CB:
330 priv->pref_frame_cb = g_value_get_pointer(value);
331 break;
332 case PROP_PREF_REQUEST_CB:
333 priv->pref_request_cb = g_value_get_pointer(value);
334 break;
335 case PROP_FLAGS:
336 priv->flags = g_value_get_flags(value);
337 break;
338 default:
339 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
340 break;
341 }
342 }
343
344 /* Get method for GObject properties */
345 static void
346 purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
347 GParamSpec *pspec)
348 {
349 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
350
351 switch (param_id) {
352 case PROP_ACTIONS_CB:
353 g_value_set_pointer(value,
354 purple_plugin_info_get_actions_cb(info));
355 break;
356 case PROP_EXTRA_CB:
357 g_value_set_pointer(value,
358 purple_plugin_info_get_extra_cb(info));
359 break;
360 case PROP_PREF_FRAME_CB:
361 g_value_set_pointer(value,
362 purple_plugin_info_get_pref_frame_cb(info));
363 break;
364 case PROP_PREF_REQUEST_CB:
365 g_value_set_pointer(value,
366 purple_plugin_info_get_pref_request_cb(info));
367 break;
368 case PROP_FLAGS:
369 g_value_set_flags(value, purple_plugin_info_get_flags(info));
370 break;
371 default:
372 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
373 break;
374 }
375 }
376
377 /* Called when done constructing */
378 static void
379 purple_plugin_info_constructed(GObject *object)
380 {
381 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
382 GPluginPluginInfo *ginfo = GPLUGIN_PLUGIN_INFO(info);
383 PurplePluginInfoPrivate *priv =
384 purple_plugin_info_get_instance_private(info);
385 const char *id = gplugin_plugin_info_get_id(ginfo);
386 guint32 version;
387
388 G_OBJECT_CLASS(purple_plugin_info_parent_class)->constructed(object);
389
390 if (id == NULL || *id == '\0')
391 priv->error = g_strdup(_("This plugin has not defined an ID."));
392
393 if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
394 {
395 priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
396 purple_core_get_ui(), priv->ui_requirement);
397 purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
398 id, priv->error);
399 }
400
401 version = gplugin_plugin_info_get_abi_version(ginfo);
402 if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
403 PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
404 {
405 priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
406 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
407 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
408 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
409 purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
410 id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
411 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
412 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
413 }
414 }
415
416 /* GObject finalize function */
417 static void
418 purple_plugin_info_finalize(GObject *object)
419 {
420 PurplePluginInfoPrivate *priv =
421 purple_plugin_info_get_instance_private(
422 PURPLE_PLUGIN_INFO(object));
423
424 g_free(priv->ui_requirement);
425 g_free(priv->error);
426
427 G_OBJECT_CLASS(purple_plugin_info_parent_class)->finalize(object);
428 }
429
430 /* Class initializer function */
431 static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
432 {
433 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
434
435 obj_class->constructed = purple_plugin_info_constructed;
436 obj_class->finalize = purple_plugin_info_finalize;
437
438 /* Setup properties */
439 obj_class->get_property = purple_plugin_info_get_property;
440 obj_class->set_property = purple_plugin_info_set_property;
441
442 g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
443 g_param_spec_string("ui-requirement",
444 "UI Requirement",
445 "ID of UI that is required by this plugin", NULL,
446 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
447
448 g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
449 g_param_spec_pointer("actions-cb",
450 "Plugin actions",
451 "Callback that returns list of plugin's actions",
452 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
453 G_PARAM_STATIC_STRINGS));
454
455 g_object_class_install_property(obj_class, PROP_EXTRA_CB,
456 g_param_spec_pointer("extra-cb",
457 "Extra info callback",
458 "Callback that returns extra info about the plugin",
459 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
460 G_PARAM_STATIC_STRINGS));
461
462 g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
463 g_param_spec_pointer("pref-frame-cb",
464 "Preferences frame callback",
465 "The callback that returns the preferences frame",
466 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
467 G_PARAM_STATIC_STRINGS));
468
469 g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
470 g_param_spec_pointer("pref-request-cb",
471 "Preferences request callback",
472 "Callback that returns preferences request handle",
473 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
474 G_PARAM_STATIC_STRINGS));
475
476 g_object_class_install_property(obj_class, PROP_FLAGS,
477 g_param_spec_flags("flags",
478 "Plugin flags",
479 "The flags for the plugin",
480 PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
481 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
482 G_PARAM_STATIC_STRINGS));
483 }
484
485 /**************************************************************************
486 * PluginInfo API
487 **************************************************************************/
488 GPluginPluginInfo *
489 purple_plugin_info_new(const char *first_property, ...)
490 {
491 GObject *info;
492 va_list var_args;
493
494 /* at least ID is required */
495 if (!first_property)
496 return NULL;
497
498 va_start(var_args, first_property);
499 info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
500 var_args);
501 va_end(var_args);
502
503 return GPLUGIN_PLUGIN_INFO(info);
504 }
505
506 PurplePluginActionsCb
507 purple_plugin_info_get_actions_cb(PurplePluginInfo *info)
508 {
509 PurplePluginInfoPrivate *priv = NULL;
510
511 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
512
513 priv = purple_plugin_info_get_instance_private(info);
514 return priv->actions_cb;
515 }
516
517 PurplePluginExtraCb
518 purple_plugin_info_get_extra_cb(PurplePluginInfo *info)
519 {
520 PurplePluginInfoPrivate *priv = NULL;
521
522 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
523
524 priv = purple_plugin_info_get_instance_private(info);
525 return priv->extra_cb;
526 }
527
528 PurplePluginPrefFrameCb
529 purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info)
530 {
531 PurplePluginInfoPrivate *priv = NULL;
532
533 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
534
535 priv = purple_plugin_info_get_instance_private(info);
536 return priv->pref_frame_cb;
537 }
538
539 PurplePluginPrefRequestCb
540 purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info)
541 {
542 PurplePluginInfoPrivate *priv = NULL;
543
544 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
545
546 priv = purple_plugin_info_get_instance_private(info);
547 return priv->pref_request_cb;
548 }
549
550 PurplePluginInfoFlags
551 purple_plugin_info_get_flags(PurplePluginInfo *info)
552 {
553 PurplePluginInfoPrivate *priv = NULL;
554
555 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), 0);
556
557 priv = purple_plugin_info_get_instance_private(info);
558 return priv->flags;
559 }
560
561 const gchar *
562 purple_plugin_info_get_error(PurplePluginInfo *info)
563 {
564 PurplePluginInfoPrivate *priv = NULL;
565
566 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
567
568 priv = purple_plugin_info_get_instance_private(info);
569 return priv->error;
570 }
571
572 /**************************************************************************
573 * PluginAction API 255 * PluginAction API
574 **************************************************************************/ 256 **************************************************************************/
575 PurplePluginAction * 257 PurplePluginAction *
576 purple_plugin_action_new(const char* label, PurplePluginActionCb callback) 258 purple_plugin_action_new(const char* label, PurplePluginActionCb callback)
577 { 259 {
655 337
656 plugins = purple_plugins_find_all(); 338 plugins = purple_plugins_find_all();
657 for (l = plugins; l != NULL; l = l->next) { 339 for (l = plugins; l != NULL; l = l->next) {
658 PurplePlugin *plugin = PURPLE_PLUGIN(l->data); 340 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
659 PurplePluginInfo *info; 341 PurplePluginInfo *info;
660 PurplePluginInfoPrivate *priv; 342 PurplePluginInfoFlags flags;
343 gboolean unloaded;
661 344
662 if (purple_plugin_is_loaded(plugin)) 345 if (purple_plugin_is_loaded(plugin))
663 continue; 346 continue;
664 347
665 info = purple_plugin_get_info(plugin); 348 info = purple_plugin_get_info(plugin);
666 priv = purple_plugin_info_get_instance_private(info); 349
667 350 unloaded = purple_plugin_info_get_unloaded(info);
668 if (!priv->unloaded && purple_plugin_info_get_flags(info) & 351 flags = purple_plugin_info_get_flags(info);
669 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) { 352 if (!unloaded && flags & PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
670 gchar *filename = gplugin_plugin_get_filename(plugin); 353 gchar *filename = gplugin_plugin_get_filename(plugin);
671 purple_debug_info("plugins", "Auto-loading plugin %s\n", 354 purple_debug_info("plugins", "Auto-loading plugin %s\n",
672 filename); 355 filename);
673 purple_plugin_load(plugin, NULL); 356 purple_plugin_load(plugin, NULL);
674 g_free(filename); 357 g_free(filename);

mercurial