comparison libpurple/plugin.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 21bc8d84974f
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /*
2 * gaim
3 *
4 * Gaim is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22 #include "internal.h"
23
24 #include "accountopt.h"
25 #include "core.h"
26 #include "dbus-maybe.h"
27 #include "debug.h"
28 #include "notify.h"
29 #include "prefs.h"
30 #include "prpl.h"
31 #include "request.h"
32 #include "signals.h"
33 #include "util.h"
34 #include "version.h"
35
36 typedef struct
37 {
38 GHashTable *commands;
39 size_t command_count;
40
41 } GaimPluginIpcInfo;
42
43 typedef struct
44 {
45 GaimCallback func;
46 GaimSignalMarshalFunc marshal;
47
48 int num_params;
49 GaimValue **params;
50 GaimValue *ret_value;
51
52 } GaimPluginIpcCommand;
53
54 static GList *search_paths = NULL;
55 static GList *plugins = NULL;
56 static GList *loaded_plugins = NULL;
57 static GList *protocol_plugins = NULL;
58 #ifdef GAIM_PLUGINS
59 static GList *load_queue = NULL;
60 static GList *plugin_loaders = NULL;
61 #endif
62
63 /*
64 * TODO: I think the intention was to allow multiple load and unload
65 * callback functions. Perhaps using a GList instead of a
66 * pointer to a single function.
67 */
68 static void (*probe_cb)(void *) = NULL;
69 static void *probe_cb_data = NULL;
70 static void (*load_cb)(GaimPlugin *, void *) = NULL;
71 static void *load_cb_data = NULL;
72 static void (*unload_cb)(GaimPlugin *, void *) = NULL;
73 static void *unload_cb_data = NULL;
74
75 #ifdef GAIM_PLUGINS
76
77 static gboolean
78 has_file_extension(const char *filename, const char *ext)
79 {
80 int len, extlen;
81
82 if (filename == NULL || *filename == '\0' || ext == NULL)
83 return 0;
84
85 extlen = strlen(ext);
86 len = strlen(filename) - extlen;
87
88 if (len < 0)
89 return 0;
90
91 return (strncmp(filename + len, ext, extlen) == 0);
92 }
93
94 static gboolean
95 is_native(const char *filename)
96 {
97 const char *last_period;
98
99 last_period = strrchr(filename, '.');
100 if (last_period == NULL)
101 return FALSE;
102
103 return !(strcmp(last_period, ".dll") &
104 strcmp(last_period, ".sl") &
105 strcmp(last_period, ".so"));
106 }
107
108 static char *
109 gaim_plugin_get_basename(const char *filename)
110 {
111 const char *basename;
112 const char *last_period;
113
114 basename = strrchr(filename, G_DIR_SEPARATOR);
115 if (basename != NULL)
116 basename++;
117 else
118 basename = filename;
119
120 if (is_native(basename) &&
121 ((last_period = strrchr(basename, '.')) != NULL))
122 return g_strndup(basename, (last_period - basename));
123
124 return g_strdup(basename);
125 }
126
127 static gboolean
128 loader_supports_file(GaimPlugin *loader, const char *filename)
129 {
130 GList *exts;
131
132 for (exts = GAIM_PLUGIN_LOADER_INFO(loader)->exts; exts != NULL; exts = exts->next) {
133 if (has_file_extension(filename, (char *)exts->data)) {
134 return TRUE;
135 }
136 }
137
138 return FALSE;
139 }
140
141 static GaimPlugin *
142 find_loader_for_plugin(const GaimPlugin *plugin)
143 {
144 GaimPlugin *loader;
145 GList *l;
146
147 if (plugin->path == NULL)
148 return NULL;
149
150 for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) {
151 loader = l->data;
152
153 if (loader->info->type == GAIM_PLUGIN_LOADER &&
154 loader_supports_file(loader, plugin->path)) {
155
156 return loader;
157 }
158
159 loader = NULL;
160 }
161
162 return NULL;
163 }
164
165 #endif /* GAIM_PLUGINS */
166
167 /**
168 * Negative if a before b, 0 if equal, positive if a after b.
169 */
170 static gint
171 compare_prpl(GaimPlugin *a, GaimPlugin *b)
172 {
173 if(GAIM_IS_PROTOCOL_PLUGIN(a)) {
174 if(GAIM_IS_PROTOCOL_PLUGIN(b))
175 return strcmp(a->info->name, b->info->name);
176 else
177 return -1;
178 } else {
179 if(GAIM_IS_PROTOCOL_PLUGIN(b))
180 return 1;
181 else
182 return 0;
183 }
184 }
185
186 GaimPlugin *
187 gaim_plugin_new(gboolean native, const char *path)
188 {
189 GaimPlugin *plugin;
190
191 plugin = g_new0(GaimPlugin, 1);
192
193 plugin->native_plugin = native;
194 plugin->path = g_strdup(path);
195
196 GAIM_DBUS_REGISTER_POINTER(plugin, GaimPlugin);
197
198 return plugin;
199 }
200
201 GaimPlugin *
202 gaim_plugin_probe(const char *filename)
203 {
204 #ifdef GAIM_PLUGINS
205 GaimPlugin *plugin = NULL;
206 GaimPlugin *loader;
207 gpointer unpunned;
208 gchar *basename = NULL;
209 gboolean (*gaim_init_plugin)(GaimPlugin *);
210
211 gaim_debug_misc("plugins", "probing %s\n", filename);
212 g_return_val_if_fail(filename != NULL, NULL);
213
214 if (!g_file_test(filename, G_FILE_TEST_EXISTS))
215 return NULL;
216
217 /* If this plugin has already been probed then exit */
218 basename = gaim_plugin_get_basename(filename);
219 plugin = gaim_plugins_find_with_basename(basename);
220 g_free(basename);
221 if (plugin != NULL)
222 {
223 if (!strcmp(filename, plugin->path))
224 return plugin;
225 else if (!gaim_plugin_is_unloadable(plugin))
226 {
227 gaim_debug_info("plugins", "Not loading %s. "
228 "Another plugin with the same name (%s) has already been loaded.\n",
229 filename, plugin->path);
230 return plugin;
231 }
232 else
233 {
234 /* The old plugin was a different file and it was unloadable.
235 * There's no guarantee that this new file with the same name
236 * will be loadable, but unless it fails in one of the silent
237 * ways and the first one didn't, it's not any worse. The user
238 * will still see a greyed-out plugin, which is what we want. */
239 gaim_plugin_destroy(plugin);
240 }
241 }
242
243 plugin = gaim_plugin_new(has_file_extension(filename, G_MODULE_SUFFIX), filename);
244
245 if (plugin->native_plugin) {
246 const char *error;
247 #ifdef _WIN32
248 /* Suppress error popups for failing to load plugins */
249 UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
250 #endif
251
252 /*
253 * We pass G_MODULE_BIND_LOCAL here to prevent symbols from
254 * plugins being added to the global name space.
255 *
256 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
257 * TODO: I guess there's nothing we can do about that?
258 */
259 #if GLIB_CHECK_VERSION(2,3,3)
260 plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL);
261 #else
262 plugin->handle = g_module_open(filename, 0);
263 #endif
264
265 if (plugin->handle == NULL)
266 {
267 const char *error = g_module_error();
268 if (error != NULL && gaim_str_has_prefix(error, filename))
269 {
270 error = error + strlen(filename);
271
272 /* These are just so we don't crash. If we
273 * got this far, they should always be true. */
274 if (*error == ':')
275 error++;
276 if (*error == ' ')
277 error++;
278 }
279
280 if (error == NULL || !*error)
281 {
282 plugin->error = g_strdup(_("Unknown error"));
283 gaim_debug_error("plugins", "%s is not loadable: Unknown error\n",
284 plugin->path);
285 }
286 else
287 {
288 plugin->error = g_strdup(error);
289 gaim_debug_error("plugins", "%s is not loadable: %s\n",
290 plugin->path, plugin->error);
291 }
292 #if GLIB_CHECK_VERSION(2,3,3)
293 plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
294 #else
295 plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY);
296 #endif
297
298 if (plugin->handle == NULL)
299 {
300 #ifdef _WIN32
301 /* Restore the original error mode */
302 SetErrorMode(old_error_mode);
303 #endif
304 gaim_plugin_destroy(plugin);
305 return NULL;
306 }
307 else
308 {
309 /* We were able to load the plugin with lazy symbol binding.
310 * This means we're missing some symbol. Mark it as
311 * unloadable and keep going so we get the info to display
312 * to the user so they know to rebuild this plugin. */
313 plugin->unloadable = TRUE;
314 }
315 }
316
317 if (!g_module_symbol(plugin->handle, "gaim_init_plugin",
318 &unpunned))
319 {
320 gaim_debug_error("plugins", "%s is not usable because the "
321 "'gaim_init_plugin' symbol could not be "
322 "found. Does the plugin call the "
323 "GAIM_INIT_PLUGIN() macro?\n", plugin->path);
324
325 g_module_close(plugin->handle);
326 error = g_module_error();
327 if (error != NULL)
328 gaim_debug_error("plugins", "Error closing module %s: %s\n",
329 plugin->path, error);
330 plugin->handle = NULL;
331
332 #ifdef _WIN32
333 /* Restore the original error mode */
334 SetErrorMode(old_error_mode);
335 #endif
336 gaim_plugin_destroy(plugin);
337 return NULL;
338 }
339 gaim_init_plugin = unpunned;
340
341 #ifdef _WIN32
342 /* Restore the original error mode */
343 SetErrorMode(old_error_mode);
344 #endif
345 }
346 else {
347 loader = find_loader_for_plugin(plugin);
348
349 if (loader == NULL) {
350 gaim_plugin_destroy(plugin);
351 return NULL;
352 }
353
354 gaim_init_plugin = GAIM_PLUGIN_LOADER_INFO(loader)->probe;
355 }
356
357 if (!gaim_init_plugin(plugin) || plugin->info == NULL)
358 {
359 gaim_plugin_destroy(plugin);
360 return NULL;
361 }
362 else if (plugin->info->ui_requirement &&
363 strcmp(plugin->info->ui_requirement, gaim_core_get_ui()))
364 {
365 plugin->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
366 gaim_core_get_ui(), plugin->info->ui_requirement);
367 gaim_debug_error("plugins", "%s is not loadable: The UI requirement is not met.\n", plugin->path);
368 plugin->unloadable = TRUE;
369 return plugin;
370 }
371
372 /* Really old plugins. */
373 if (plugin->info->magic != GAIM_PLUGIN_MAGIC)
374 {
375 if (plugin->info->magic >= 2 && plugin->info->magic <= 4)
376 {
377 struct _GaimPluginInfo2
378 {
379 unsigned int api_version;
380 GaimPluginType type;
381 char *ui_requirement;
382 unsigned long flags;
383 GList *dependencies;
384 GaimPluginPriority priority;
385
386 char *id;
387 char *name;
388 char *version;
389 char *summary;
390 char *description;
391 char *author;
392 char *homepage;
393
394 gboolean (*load)(GaimPlugin *plugin);
395 gboolean (*unload)(GaimPlugin *plugin);
396 void (*destroy)(GaimPlugin *plugin);
397
398 void *ui_info;
399 void *extra_info;
400 GaimPluginUiInfo *prefs_info;
401 GList *(*actions)(GaimPlugin *plugin, gpointer context);
402 } *info2 = (struct _GaimPluginInfo2 *)plugin->info;
403
404 /* This leaks... but only for ancient plugins, so deal with it. */
405 plugin->info = g_new0(GaimPluginInfo, 1);
406
407 /* We don't really need all these to display the plugin info, but
408 * I'm copying them all for good measure. */
409 plugin->info->magic = info2->api_version;
410 plugin->info->type = info2->type;
411 plugin->info->ui_requirement = info2->ui_requirement;
412 plugin->info->flags = info2->flags;
413 plugin->info->dependencies = info2->dependencies;
414 plugin->info->id = info2->id;
415 plugin->info->name = info2->name;
416 plugin->info->version = info2->version;
417 plugin->info->summary = info2->summary;
418 plugin->info->description = info2->description;
419 plugin->info->author = info2->author;
420 plugin->info->homepage = info2->homepage;
421 plugin->info->load = info2->load;
422 plugin->info->unload = info2->unload;
423 plugin->info->destroy = info2->destroy;
424 plugin->info->ui_info = info2->ui_info;
425 plugin->info->extra_info = info2->extra_info;
426
427 if (info2->api_version >= 3)
428 plugin->info->prefs_info = info2->prefs_info;
429
430 if (info2->api_version >= 4)
431 plugin->info->actions = info2->actions;
432
433
434 plugin->error = g_strdup_printf(_("Plugin magic mismatch %d (need %d)"),
435 plugin->info->magic, GAIM_PLUGIN_MAGIC);
436 gaim_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
437 plugin->path, plugin->info->magic, GAIM_PLUGIN_MAGIC);
438 plugin->unloadable = TRUE;
439 return plugin;
440 }
441
442 gaim_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
443 plugin->path, plugin->info->magic, GAIM_PLUGIN_MAGIC);
444 gaim_plugin_destroy(plugin);
445 return NULL;
446 }
447
448 if (plugin->info->major_version != GAIM_MAJOR_VERSION ||
449 plugin->info->minor_version > GAIM_MINOR_VERSION)
450 {
451 plugin->error = g_strdup_printf(_("ABI version mismatch %d.%d.x (need %d.%d.x)"),
452 plugin->info->major_version, plugin->info->minor_version,
453 GAIM_MAJOR_VERSION, GAIM_MINOR_VERSION);
454 gaim_debug_error("plugins", "%s is not loadable: ABI version mismatch %d.%d.x (need %d.%d.x)\n",
455 plugin->path, plugin->info->major_version, plugin->info->minor_version,
456 GAIM_MAJOR_VERSION, GAIM_MINOR_VERSION);
457 plugin->unloadable = TRUE;
458 return plugin;
459 }
460
461 if (plugin->info->type == GAIM_PLUGIN_PROTOCOL)
462 {
463 /* If plugin is a PRPL, make sure it implements the required functions */
464 if ((GAIM_PLUGIN_PROTOCOL_INFO(plugin)->list_icon == NULL) ||
465 (GAIM_PLUGIN_PROTOCOL_INFO(plugin)->login == NULL) ||
466 (GAIM_PLUGIN_PROTOCOL_INFO(plugin)->close == NULL))
467 {
468 plugin->error = g_strdup(_("Plugin does not implement all required functions"));
469 gaim_debug_error("plugins", "%s is not loadable: Plugin does not implement all required functions\n",
470 plugin->path);
471 plugin->unloadable = TRUE;
472 return plugin;
473 }
474
475 /* For debugging, let's warn about prpl prefs. */
476 if (plugin->info->prefs_info != NULL)
477 {
478 gaim_debug_error("plugins", "%s has a prefs_info, but is a prpl. This is no longer supported.\n",
479 plugin->path);
480 }
481 }
482
483 return plugin;
484 #else
485 return NULL;
486 #endif /* !GAIM_PLUGINS */
487 }
488
489 #ifdef GAIM_PLUGINS
490 static gint
491 compare_plugins(gconstpointer a, gconstpointer b)
492 {
493 const GaimPlugin *plugina = a;
494 const GaimPlugin *pluginb = b;
495
496 return strcmp(plugina->info->name, pluginb->info->name);
497 }
498 #endif /* GAIM_PLUGINS */
499
500 gboolean
501 gaim_plugin_load(GaimPlugin *plugin)
502 {
503 #ifdef GAIM_PLUGINS
504 GList *dep_list = NULL;
505 GList *l;
506
507 g_return_val_if_fail(plugin != NULL, FALSE);
508
509 if (gaim_plugin_is_loaded(plugin))
510 return TRUE;
511
512 if (gaim_plugin_is_unloadable(plugin))
513 return FALSE;
514
515 g_return_val_if_fail(plugin->error == NULL, FALSE);
516
517 /*
518 * Go through the list of the plugin's dependencies.
519 *
520 * First pass: Make sure all the plugins needed are probed.
521 */
522 for (l = plugin->info->dependencies; l != NULL; l = l->next)
523 {
524 const char *dep_name = (const char *)l->data;
525 GaimPlugin *dep_plugin;
526
527 dep_plugin = gaim_plugins_find_with_id(dep_name);
528
529 if (dep_plugin == NULL)
530 {
531 char *tmp;
532
533 tmp = g_strdup_printf(_("The required plugin %s was not found. "
534 "Please install this plugin and try again."),
535 dep_name);
536
537 gaim_notify_error(NULL, NULL,
538 _("Gaim encountered errors loading the plugin."), tmp);
539 g_free(tmp);
540
541 g_list_free(dep_list);
542
543 return FALSE;
544 }
545
546 dep_list = g_list_append(dep_list, dep_plugin);
547 }
548
549 /* Second pass: load all the required plugins. */
550 for (l = dep_list; l != NULL; l = l->next)
551 {
552 GaimPlugin *dep_plugin = (GaimPlugin *)l->data;
553
554 if (!gaim_plugin_is_loaded(dep_plugin))
555 {
556 if (!gaim_plugin_load(dep_plugin))
557 {
558 char *tmp;
559
560 tmp = g_strdup_printf(_("The required plugin %s was unable to load."),
561 plugin->info->name);
562
563 gaim_notify_error(NULL, NULL,
564 _("Gaim was unable to load your plugin."), tmp);
565 g_free(tmp);
566
567 g_list_free(dep_list);
568
569 return FALSE;
570 }
571 }
572 }
573
574 /* Third pass: note that other plugins are dependencies of this plugin.
575 * This is done separately in case we had to bail out earlier. */
576 for (l = dep_list; l != NULL; l = l->next)
577 {
578 GaimPlugin *dep_plugin = (GaimPlugin *)l->data;
579 dep_plugin->dependent_plugins = g_list_prepend(dep_plugin->dependent_plugins, plugin->info->id);
580 }
581
582 g_list_free(dep_list);
583
584 if (plugin->native_plugin)
585 {
586 if (plugin->info != NULL && plugin->info->load != NULL)
587 {
588 if (!plugin->info->load(plugin))
589 return FALSE;
590 }
591 }
592 else {
593 GaimPlugin *loader;
594 GaimPluginLoaderInfo *loader_info;
595
596 loader = find_loader_for_plugin(plugin);
597
598 if (loader == NULL)
599 return FALSE;
600
601 loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
602
603 if (loader_info->load != NULL)
604 {
605 if (!loader_info->load(plugin))
606 return FALSE;
607 }
608 }
609
610 loaded_plugins = g_list_insert_sorted(loaded_plugins, plugin, compare_plugins);
611
612 plugin->loaded = TRUE;
613
614 /* TODO */
615 if (load_cb != NULL)
616 load_cb(plugin, load_cb_data);
617
618 gaim_signal_emit(gaim_plugins_get_handle(), "plugin-load", plugin);
619
620 return TRUE;
621
622 #else
623 return TRUE;
624 #endif /* !GAIM_PLUGINS */
625 }
626
627 gboolean
628 gaim_plugin_unload(GaimPlugin *plugin)
629 {
630 #ifdef GAIM_PLUGINS
631 GList *l;
632
633 g_return_val_if_fail(plugin != NULL, FALSE);
634
635 loaded_plugins = g_list_remove(loaded_plugins, plugin);
636 if ((plugin->info != NULL) && GAIM_IS_PROTOCOL_PLUGIN(plugin))
637 protocol_plugins = g_list_remove(protocol_plugins, plugin);
638
639 g_return_val_if_fail(gaim_plugin_is_loaded(plugin), FALSE);
640
641 gaim_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name);
642
643 /* cancel any pending dialogs the plugin has */
644 gaim_request_close_with_handle(plugin);
645 gaim_notify_close_with_handle(plugin);
646
647 plugin->loaded = FALSE;
648
649 /* Unload all plugins that depend on this plugin. */
650 while ((l = plugin->dependent_plugins) != NULL)
651 {
652 const char * dep_name = (const char *)l->data;
653 GaimPlugin *dep_plugin;
654
655 dep_plugin = gaim_plugins_find_with_id(dep_name);
656
657 if (dep_plugin != NULL && gaim_plugin_is_loaded(dep_plugin))
658 {
659 if (!gaim_plugin_unload(dep_plugin))
660 {
661 char *translated_name = g_strdup(_(dep_plugin->info->name));
662 char *tmp;
663
664 tmp = g_strdup_printf(_("The dependent plugin %s failed to unload."),
665 translated_name);
666 g_free(translated_name);
667
668 gaim_notify_error(NULL, NULL,
669 _("Gaim encountered errors unloading the plugin."), tmp);
670 g_free(tmp);
671 }
672 }
673 }
674
675 /* Remove this plugin from each dependency's dependent_plugins list. */
676 for (l = plugin->info->dependencies; l != NULL; l = l->next)
677 {
678 const char *dep_name = (const char *)l->data;
679 GaimPlugin *dependency;
680
681 dependency = gaim_plugins_find_with_id(dep_name);
682
683 dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id);
684 }
685
686 if (plugin->native_plugin) {
687 if (plugin->info->unload != NULL)
688 plugin->info->unload(plugin);
689
690 if (plugin->info->type == GAIM_PLUGIN_PROTOCOL) {
691 GaimPluginProtocolInfo *prpl_info;
692 GList *l;
693
694 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
695
696 for (l = prpl_info->user_splits; l != NULL; l = l->next)
697 gaim_account_user_split_destroy(l->data);
698
699 for (l = prpl_info->protocol_options; l != NULL; l = l->next)
700 gaim_account_option_destroy(l->data);
701
702 if (prpl_info->user_splits != NULL) {
703 g_list_free(prpl_info->user_splits);
704 prpl_info->user_splits = NULL;
705 }
706
707 if (prpl_info->protocol_options != NULL) {
708 g_list_free(prpl_info->protocol_options);
709 prpl_info->protocol_options = NULL;
710 }
711 }
712 }
713 else {
714 GaimPlugin *loader;
715 GaimPluginLoaderInfo *loader_info;
716
717 loader = find_loader_for_plugin(plugin);
718
719 if (loader == NULL)
720 return FALSE;
721
722 loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
723
724 if (loader_info->unload != NULL)
725 loader_info->unload(plugin);
726 }
727
728 gaim_signals_disconnect_by_handle(plugin);
729 gaim_plugin_ipc_unregister_all(plugin);
730
731 /* TODO */
732 if (unload_cb != NULL)
733 unload_cb(plugin, unload_cb_data);
734
735 gaim_signal_emit(gaim_plugins_get_handle(), "plugin-unload", plugin);
736
737 gaim_prefs_disconnect_by_handle(plugin);
738
739 return TRUE;
740 #else
741 return TRUE;
742 #endif /* GAIM_PLUGINS */
743 }
744
745 gboolean
746 gaim_plugin_reload(GaimPlugin *plugin)
747 {
748 #ifdef GAIM_PLUGINS
749 g_return_val_if_fail(plugin != NULL, FALSE);
750 g_return_val_if_fail(gaim_plugin_is_loaded(plugin), FALSE);
751
752 if (!gaim_plugin_unload(plugin))
753 return FALSE;
754
755 if (!gaim_plugin_load(plugin))
756 return FALSE;
757
758 return TRUE;
759 #else
760 return TRUE;
761 #endif /* !GAIM_PLUGINS */
762 }
763
764 void
765 gaim_plugin_destroy(GaimPlugin *plugin)
766 {
767 #ifdef GAIM_PLUGINS
768 g_return_if_fail(plugin != NULL);
769
770 if (gaim_plugin_is_loaded(plugin))
771 gaim_plugin_unload(plugin);
772
773 plugins = g_list_remove(plugins, plugin);
774
775 if (load_queue != NULL)
776 load_queue = g_list_remove(load_queue, plugin);
777
778 /* true, this may leak a little memory if there is a major version
779 * mismatch, but it's a lot better than trying to free something
780 * we shouldn't, and crashing while trying to load an old plugin */
781 if(plugin->info == NULL || plugin->info->magic != GAIM_PLUGIN_MAGIC ||
782 plugin->info->major_version != GAIM_MAJOR_VERSION)
783 {
784 if(plugin->handle)
785 g_module_close(plugin->handle);
786
787 g_free(plugin->path);
788 g_free(plugin->error);
789
790 GAIM_DBUS_UNREGISTER_POINTER(plugin);
791
792 g_free(plugin);
793 return;
794 }
795
796 if (plugin->info != NULL)
797 g_list_free(plugin->info->dependencies);
798
799 if (plugin->native_plugin)
800 {
801 if (plugin->info != NULL && plugin->info->type == GAIM_PLUGIN_LOADER)
802 {
803 GaimPluginLoaderInfo *loader_info;
804 GList *exts, *l, *next_l;
805 GaimPlugin *p2;
806
807 loader_info = GAIM_PLUGIN_LOADER_INFO(plugin);
808
809 if (loader_info != NULL && loader_info->exts != NULL)
810 {
811 for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
812 exts != NULL;
813 exts = exts->next) {
814
815 for (l = gaim_plugins_get_all(); l != NULL; l = next_l)
816 {
817 next_l = l->next;
818
819 p2 = l->data;
820
821 if (p2->path != NULL &&
822 has_file_extension(p2->path, exts->data))
823 {
824 gaim_plugin_destroy(p2);
825 }
826 }
827 }
828
829 g_list_free(loader_info->exts);
830 }
831
832 plugin_loaders = g_list_remove(plugin_loaders, plugin);
833 }
834
835 if (plugin->info != NULL && plugin->info->destroy != NULL)
836 plugin->info->destroy(plugin);
837
838 if (plugin->handle != NULL)
839 g_module_close(plugin->handle);
840 }
841 else
842 {
843 GaimPlugin *loader;
844 GaimPluginLoaderInfo *loader_info;
845
846 loader = find_loader_for_plugin(plugin);
847
848 if (loader != NULL)
849 {
850 loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
851
852 if (loader_info->destroy != NULL)
853 loader_info->destroy(plugin);
854 }
855 }
856
857 g_free(plugin->path);
858 g_free(plugin->error);
859
860 GAIM_DBUS_UNREGISTER_POINTER(plugin);
861
862 g_free(plugin);
863 #endif /* !GAIM_PLUGINS */
864 }
865
866 gboolean
867 gaim_plugin_is_loaded(const GaimPlugin *plugin)
868 {
869 g_return_val_if_fail(plugin != NULL, FALSE);
870
871 return plugin->loaded;
872 }
873
874 gboolean
875 gaim_plugin_is_unloadable(const GaimPlugin *plugin)
876 {
877 g_return_val_if_fail(plugin != NULL, FALSE);
878
879 return plugin->unloadable;
880 }
881
882 const gchar *
883 gaim_plugin_get_id(const GaimPlugin *plugin) {
884 g_return_val_if_fail(plugin, NULL);
885 g_return_val_if_fail(plugin->info, NULL);
886
887 return plugin->info->id;
888 }
889
890 const gchar *
891 gaim_plugin_get_name(const GaimPlugin *plugin) {
892 g_return_val_if_fail(plugin, NULL);
893 g_return_val_if_fail(plugin->info, NULL);
894
895 return plugin->info->name;
896 }
897
898 const gchar *
899 gaim_plugin_get_version(const GaimPlugin *plugin) {
900 g_return_val_if_fail(plugin, NULL);
901 g_return_val_if_fail(plugin->info, NULL);
902
903 return plugin->info->version;
904 }
905
906 const gchar *
907 gaim_plugin_get_summary(const GaimPlugin *plugin) {
908 g_return_val_if_fail(plugin, NULL);
909 g_return_val_if_fail(plugin->info, NULL);
910
911 return plugin->info->summary;
912 }
913
914 const gchar *
915 gaim_plugin_get_description(const GaimPlugin *plugin) {
916 g_return_val_if_fail(plugin, NULL);
917 g_return_val_if_fail(plugin->info, NULL);
918
919 return plugin->info->description;
920 }
921
922 const gchar *
923 gaim_plugin_get_author(const GaimPlugin *plugin) {
924 g_return_val_if_fail(plugin, NULL);
925 g_return_val_if_fail(plugin->info, NULL);
926
927 return plugin->info->author;
928 }
929
930 const gchar *
931 gaim_plugin_get_homepage(const GaimPlugin *plugin) {
932 g_return_val_if_fail(plugin, NULL);
933 g_return_val_if_fail(plugin->info, NULL);
934
935 return plugin->info->homepage;
936 }
937
938 /**************************************************************************
939 * Plugin IPC
940 **************************************************************************/
941 static void
942 destroy_ipc_info(void *data)
943 {
944 GaimPluginIpcCommand *ipc_command = (GaimPluginIpcCommand *)data;
945 int i;
946
947 if (ipc_command->params != NULL)
948 {
949 for (i = 0; i < ipc_command->num_params; i++)
950 gaim_value_destroy(ipc_command->params[i]);
951
952 g_free(ipc_command->params);
953 }
954
955 if (ipc_command->ret_value != NULL)
956 gaim_value_destroy(ipc_command->ret_value);
957
958 g_free(ipc_command);
959 }
960
961 gboolean
962 gaim_plugin_ipc_register(GaimPlugin *plugin, const char *command,
963 GaimCallback func, GaimSignalMarshalFunc marshal,
964 GaimValue *ret_value, int num_params, ...)
965 {
966 GaimPluginIpcInfo *ipc_info;
967 GaimPluginIpcCommand *ipc_command;
968
969 g_return_val_if_fail(plugin != NULL, FALSE);
970 g_return_val_if_fail(command != NULL, FALSE);
971 g_return_val_if_fail(func != NULL, FALSE);
972 g_return_val_if_fail(marshal != NULL, FALSE);
973
974 if (plugin->ipc_data == NULL)
975 {
976 ipc_info = plugin->ipc_data = g_new0(GaimPluginIpcInfo, 1);
977 ipc_info->commands = g_hash_table_new_full(g_str_hash, g_str_equal,
978 g_free, destroy_ipc_info);
979 }
980 else
981 ipc_info = (GaimPluginIpcInfo *)plugin->ipc_data;
982
983 ipc_command = g_new0(GaimPluginIpcCommand, 1);
984 ipc_command->func = func;
985 ipc_command->marshal = marshal;
986 ipc_command->num_params = num_params;
987 ipc_command->ret_value = ret_value;
988
989 if (num_params > 0)
990 {
991 va_list args;
992 int i;
993
994 ipc_command->params = g_new0(GaimValue *, num_params);
995
996 va_start(args, num_params);
997
998 for (i = 0; i < num_params; i++)
999 ipc_command->params[i] = va_arg(args, GaimValue *);
1000
1001 va_end(args);
1002 }
1003
1004 g_hash_table_replace(ipc_info->commands, g_strdup(command), ipc_command);
1005
1006 ipc_info->command_count++;
1007
1008 return TRUE;
1009 }
1010
1011 void
1012 gaim_plugin_ipc_unregister(GaimPlugin *plugin, const char *command)
1013 {
1014 GaimPluginIpcInfo *ipc_info;
1015
1016 g_return_if_fail(plugin != NULL);
1017 g_return_if_fail(command != NULL);
1018
1019 ipc_info = (GaimPluginIpcInfo *)plugin->ipc_data;
1020
1021 if (ipc_info == NULL ||
1022 g_hash_table_lookup(ipc_info->commands, command) == NULL)
1023 {
1024 gaim_debug_error("plugins",
1025 "IPC command '%s' was not registered for plugin %s\n",
1026 command, plugin->info->name);
1027 return;
1028 }
1029
1030 g_hash_table_remove(ipc_info->commands, command);
1031
1032 ipc_info->command_count--;
1033
1034 if (ipc_info->command_count == 0)
1035 {
1036 g_hash_table_destroy(ipc_info->commands);
1037 g_free(ipc_info);
1038
1039 plugin->ipc_data = NULL;
1040 }
1041 }
1042
1043 void
1044 gaim_plugin_ipc_unregister_all(GaimPlugin *plugin)
1045 {
1046 GaimPluginIpcInfo *ipc_info;
1047
1048 g_return_if_fail(plugin != NULL);
1049
1050 if (plugin->ipc_data == NULL)
1051 return; /* Silently ignore it. */
1052
1053 ipc_info = (GaimPluginIpcInfo *)plugin->ipc_data;
1054
1055 g_hash_table_destroy(ipc_info->commands);
1056 g_free(ipc_info);
1057
1058 plugin->ipc_data = NULL;
1059 }
1060
1061 gboolean
1062 gaim_plugin_ipc_get_params(GaimPlugin *plugin, const char *command,
1063 GaimValue **ret_value, int *num_params,
1064 GaimValue ***params)
1065 {
1066 GaimPluginIpcInfo *ipc_info;
1067 GaimPluginIpcCommand *ipc_command;
1068
1069 g_return_val_if_fail(plugin != NULL, FALSE);
1070 g_return_val_if_fail(command != NULL, FALSE);
1071
1072 ipc_info = (GaimPluginIpcInfo *)plugin->ipc_data;
1073
1074 if (ipc_info == NULL ||
1075 (ipc_command = g_hash_table_lookup(ipc_info->commands,
1076 command)) == NULL)
1077 {
1078 gaim_debug_error("plugins",
1079 "IPC command '%s' was not registered for plugin %s\n",
1080 command, plugin->info->name);
1081
1082 return FALSE;
1083 }
1084
1085 if (num_params != NULL)
1086 *num_params = ipc_command->num_params;
1087
1088 if (params != NULL)
1089 *params = ipc_command->params;
1090
1091 if (ret_value != NULL)
1092 *ret_value = ipc_command->ret_value;
1093
1094 return TRUE;
1095 }
1096
1097 void *
1098 gaim_plugin_ipc_call(GaimPlugin *plugin, const char *command,
1099 gboolean *ok, ...)
1100 {
1101 GaimPluginIpcInfo *ipc_info;
1102 GaimPluginIpcCommand *ipc_command;
1103 va_list args;
1104 void *ret_value;
1105
1106 if (ok != NULL)
1107 *ok = FALSE;
1108
1109 g_return_val_if_fail(plugin != NULL, NULL);
1110 g_return_val_if_fail(command != NULL, NULL);
1111
1112 ipc_info = (GaimPluginIpcInfo *)plugin->ipc_data;
1113
1114 if (ipc_info == NULL ||
1115 (ipc_command = g_hash_table_lookup(ipc_info->commands,
1116 command)) == NULL)
1117 {
1118 gaim_debug_error("plugins",
1119 "IPC command '%s' was not registered for plugin %s\n",
1120 command, plugin->info->name);
1121
1122 return NULL;
1123 }
1124
1125 va_start(args, ok);
1126 ipc_command->marshal(ipc_command->func, args, NULL, &ret_value);
1127 va_end(args);
1128
1129 if (ok != NULL)
1130 *ok = TRUE;
1131
1132 return ret_value;
1133 }
1134
1135 /**************************************************************************
1136 * Plugins subsystem
1137 **************************************************************************/
1138 void *
1139 gaim_plugins_get_handle(void) {
1140 static int handle;
1141
1142 return &handle;
1143 }
1144
1145 void
1146 gaim_plugins_init(void) {
1147 void *handle = gaim_plugins_get_handle();
1148
1149 gaim_signal_register(handle, "plugin-load",
1150 gaim_marshal_VOID__POINTER,
1151 NULL, 1,
1152 gaim_value_new(GAIM_TYPE_SUBTYPE,
1153 GAIM_SUBTYPE_PLUGIN));
1154 gaim_signal_register(handle, "plugin-unload",
1155 gaim_marshal_VOID__POINTER,
1156 NULL, 1,
1157 gaim_value_new(GAIM_TYPE_SUBTYPE,
1158 GAIM_SUBTYPE_PLUGIN));
1159 }
1160
1161 void
1162 gaim_plugins_uninit(void) {
1163 gaim_signals_disconnect_by_handle(gaim_plugins_get_handle());
1164 }
1165
1166 /**************************************************************************
1167 * Plugins API
1168 **************************************************************************/
1169 void
1170 gaim_plugins_add_search_path(const char *path)
1171 {
1172 g_return_if_fail(path != NULL);
1173
1174 if (g_list_find_custom(search_paths, path, (GCompareFunc)strcmp))
1175 return;
1176
1177 search_paths = g_list_append(search_paths, strdup(path));
1178 }
1179
1180 void
1181 gaim_plugins_unload_all(void)
1182 {
1183 #ifdef GAIM_PLUGINS
1184
1185 while (loaded_plugins != NULL)
1186 gaim_plugin_unload(loaded_plugins->data);
1187
1188 #endif /* GAIM_PLUGINS */
1189 }
1190
1191 void
1192 gaim_plugins_destroy_all(void)
1193 {
1194 #ifdef GAIM_PLUGINS
1195
1196 while (plugins != NULL)
1197 gaim_plugin_destroy(plugins->data);
1198
1199 #endif /* GAIM_PLUGINS */
1200 }
1201
1202 void
1203 gaim_plugins_save_loaded(const char *key)
1204 {
1205 #ifdef GAIM_PLUGINS
1206 GList *pl;
1207 GList *files = NULL;
1208 GaimPlugin *p;
1209
1210 for (pl = gaim_plugins_get_loaded(); pl != NULL; pl = pl->next) {
1211 p = pl->data;
1212
1213 if (p->info->type != GAIM_PLUGIN_PROTOCOL &&
1214 p->info->type != GAIM_PLUGIN_LOADER) {
1215 files = g_list_append(files, p->path);
1216 }
1217 }
1218
1219 gaim_prefs_set_path_list(key, files);
1220 g_list_free(files);
1221 #endif
1222 }
1223
1224 void
1225 gaim_plugins_load_saved(const char *key)
1226 {
1227 #ifdef GAIM_PLUGINS
1228 GList *f, *files;
1229
1230 g_return_if_fail(key != NULL);
1231
1232 files = gaim_prefs_get_path_list(key);
1233
1234 for (f = files; f; f = f->next)
1235 {
1236 char *filename;
1237 char *basename;
1238 GaimPlugin *plugin;
1239
1240 if (f->data == NULL)
1241 continue;
1242
1243 filename = f->data;
1244
1245 /*
1246 * We don't know if the filename uses Windows or Unix path
1247 * separators (because people might be sharing a prefs.xml
1248 * file across systems), so we find the last occurrence
1249 * of either.
1250 */
1251 basename = strrchr(filename, '/');
1252 if ((basename == NULL) || (basename < strrchr(filename, '\\')))
1253 basename = strrchr(filename, '\\');
1254 if (basename != NULL)
1255 basename++;
1256
1257 /* Strip the extension */
1258 if (basename)
1259 basename = gaim_plugin_get_basename(filename);
1260
1261 if ((plugin = gaim_plugins_find_with_filename(filename)) != NULL)
1262 {
1263 gaim_debug_info("plugins", "Loading saved plugin %s\n",
1264 plugin->path);
1265 gaim_plugin_load(plugin);
1266 }
1267 else if (basename && (plugin = gaim_plugins_find_with_basename(basename)) != NULL)
1268 {
1269 gaim_debug_info("plugins", "Loading saved plugin %s\n",
1270 plugin->path);
1271 gaim_plugin_load(plugin);
1272 }
1273 else
1274 {
1275 gaim_debug_error("plugins", "Unable to find saved plugin %s\n",
1276 filename);
1277 }
1278
1279 g_free(basename);
1280
1281 g_free(f->data);
1282 }
1283
1284 g_list_free(files);
1285 #endif /* GAIM_PLUGINS */
1286 }
1287
1288
1289 void
1290 gaim_plugins_probe(const char *ext)
1291 {
1292 #ifdef GAIM_PLUGINS
1293 GDir *dir;
1294 const gchar *file;
1295 gchar *path;
1296 GaimPlugin *plugin;
1297 GList *cur;
1298 const char *search_path;
1299
1300 if (!g_module_supported())
1301 return;
1302
1303 /* Probe plugins */
1304 for (cur = search_paths; cur != NULL; cur = cur->next)
1305 {
1306 search_path = cur->data;
1307
1308 dir = g_dir_open(search_path, 0, NULL);
1309
1310 if (dir != NULL)
1311 {
1312 while ((file = g_dir_read_name(dir)) != NULL)
1313 {
1314 path = g_build_filename(search_path, file, NULL);
1315
1316 if (ext == NULL || has_file_extension(file, ext))
1317 plugin = gaim_plugin_probe(path);
1318
1319 g_free(path);
1320 }
1321
1322 g_dir_close(dir);
1323 }
1324 }
1325
1326 /* See if we have any plugins waiting to load */
1327 while (load_queue != NULL)
1328 {
1329 plugin = (GaimPlugin *)load_queue->data;
1330
1331 load_queue = g_list_remove(load_queue, plugin);
1332
1333 if (plugin == NULL || plugin->info == NULL)
1334 continue;
1335
1336 if (plugin->info->type == GAIM_PLUGIN_LOADER)
1337 {
1338 /* We'll just load this right now. */
1339 if (!gaim_plugin_load(plugin))
1340 {
1341 gaim_plugin_destroy(plugin);
1342
1343 continue;
1344 }
1345
1346 plugin_loaders = g_list_append(plugin_loaders, plugin);
1347
1348 for (cur = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
1349 cur != NULL;
1350 cur = cur->next)
1351 {
1352 gaim_plugins_probe(cur->data);
1353 }
1354 }
1355 else if (plugin->info->type == GAIM_PLUGIN_PROTOCOL)
1356 {
1357 /* We'll just load this right now. */
1358 if (!gaim_plugin_load(plugin))
1359 {
1360 gaim_plugin_destroy(plugin);
1361
1362 continue;
1363 }
1364
1365 /* Make sure we don't load two PRPLs with the same name? */
1366 if (gaim_find_prpl(plugin->info->id))
1367 {
1368 /* Nothing to see here--move along, move along */
1369 gaim_plugin_destroy(plugin);
1370
1371 continue;
1372 }
1373
1374 protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
1375 (GCompareFunc)compare_prpl);
1376 }
1377 }
1378
1379 if (probe_cb != NULL)
1380 probe_cb(probe_cb_data);
1381 #endif /* GAIM_PLUGINS */
1382 }
1383
1384 gboolean
1385 gaim_plugin_register(GaimPlugin *plugin)
1386 {
1387 g_return_val_if_fail(plugin != NULL, FALSE);
1388
1389 /* If this plugin has been registered already then exit */
1390 if (g_list_find(plugins, plugin))
1391 return TRUE;
1392
1393 /* Ensure the plugin has the requisite information */
1394 if (plugin->info->type == GAIM_PLUGIN_LOADER)
1395 {
1396 GaimPluginLoaderInfo *loader_info;
1397
1398 loader_info = GAIM_PLUGIN_LOADER_INFO(plugin);
1399
1400 if (loader_info == NULL)
1401 {
1402 gaim_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n",
1403 plugin->path);
1404 return FALSE;
1405 }
1406 }
1407 else if (plugin->info->type == GAIM_PLUGIN_PROTOCOL)
1408 {
1409 GaimPluginProtocolInfo *prpl_info;
1410
1411 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
1412
1413 if (prpl_info == NULL)
1414 {
1415 gaim_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n",
1416 plugin->path);
1417 return FALSE;
1418 }
1419 }
1420
1421 #ifdef GAIM_PLUGINS
1422 /* This plugin should be probed and maybe loaded--add it to the queue */
1423 load_queue = g_list_append(load_queue, plugin);
1424 #else
1425 if (plugin->info != NULL)
1426 {
1427 if (plugin->info->type == GAIM_PLUGIN_PROTOCOL)
1428 protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
1429 (GCompareFunc)compare_prpl);
1430 if (plugin->info->load != NULL)
1431 if (!plugin->info->load(plugin))
1432 return FALSE;
1433 }
1434 #endif
1435
1436 plugins = g_list_append(plugins, plugin);
1437
1438 return TRUE;
1439 }
1440
1441 gboolean
1442 gaim_plugins_enabled(void)
1443 {
1444 #ifdef GAIM_PLUGINS
1445 return TRUE;
1446 #else
1447 return FALSE;
1448 #endif
1449 }
1450
1451 void
1452 gaim_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
1453 {
1454 /* TODO */
1455 probe_cb = func;
1456 probe_cb_data = data;
1457 }
1458
1459 void
1460 gaim_plugins_unregister_probe_notify_cb(void (*func)(void *))
1461 {
1462 /* TODO */
1463 probe_cb = NULL;
1464 probe_cb_data = NULL;
1465 }
1466
1467 void
1468 gaim_plugins_register_load_notify_cb(void (*func)(GaimPlugin *, void *),
1469 void *data)
1470 {
1471 /* TODO */
1472 load_cb = func;
1473 load_cb_data = data;
1474 }
1475
1476 void
1477 gaim_plugins_unregister_load_notify_cb(void (*func)(GaimPlugin *, void *))
1478 {
1479 /* TODO */
1480 load_cb = NULL;
1481 load_cb_data = NULL;
1482 }
1483
1484 void
1485 gaim_plugins_register_unload_notify_cb(void (*func)(GaimPlugin *, void *),
1486 void *data)
1487 {
1488 /* TODO */
1489 unload_cb = func;
1490 unload_cb_data = data;
1491 }
1492
1493 void
1494 gaim_plugins_unregister_unload_notify_cb(void (*func)(GaimPlugin *, void *))
1495 {
1496 /* TODO */
1497 unload_cb = NULL;
1498 unload_cb_data = NULL;
1499 }
1500
1501 GaimPlugin *
1502 gaim_plugins_find_with_name(const char *name)
1503 {
1504 GaimPlugin *plugin;
1505 GList *l;
1506
1507 for (l = plugins; l != NULL; l = l->next) {
1508 plugin = l->data;
1509
1510 if (!strcmp(plugin->info->name, name))
1511 return plugin;
1512 }
1513
1514 return NULL;
1515 }
1516
1517 GaimPlugin *
1518 gaim_plugins_find_with_filename(const char *filename)
1519 {
1520 GaimPlugin *plugin;
1521 GList *l;
1522
1523 for (l = plugins; l != NULL; l = l->next) {
1524 plugin = l->data;
1525
1526 if (plugin->path != NULL && !strcmp(plugin->path, filename))
1527 return plugin;
1528 }
1529
1530 return NULL;
1531 }
1532
1533 GaimPlugin *
1534 gaim_plugins_find_with_basename(const char *basename)
1535 {
1536 #ifdef GAIM_PLUGINS
1537 GaimPlugin *plugin;
1538 GList *l;
1539 char *tmp;
1540
1541 g_return_val_if_fail(basename != NULL, NULL);
1542
1543 for (l = plugins; l != NULL; l = l->next)
1544 {
1545 plugin = (GaimPlugin *)l->data;
1546
1547 if (plugin->path != NULL) {
1548 tmp = gaim_plugin_get_basename(plugin->path);
1549 if (!strcmp(tmp, basename))
1550 {
1551 g_free(tmp);
1552 return plugin;
1553 }
1554 g_free(tmp);
1555 }
1556 }
1557
1558 #endif /* GAIM_PLUGINS */
1559
1560 return NULL;
1561 }
1562
1563 GaimPlugin *
1564 gaim_plugins_find_with_id(const char *id)
1565 {
1566 GaimPlugin *plugin;
1567 GList *l;
1568
1569 g_return_val_if_fail(id != NULL, NULL);
1570
1571 for (l = plugins; l != NULL; l = l->next)
1572 {
1573 plugin = l->data;
1574
1575 if (plugin->info->id != NULL && !strcmp(plugin->info->id, id))
1576 return plugin;
1577 }
1578
1579 return NULL;
1580 }
1581
1582 GList *
1583 gaim_plugins_get_loaded(void)
1584 {
1585 return loaded_plugins;
1586 }
1587
1588 GList *
1589 gaim_plugins_get_protocols(void)
1590 {
1591 return protocol_plugins;
1592 }
1593
1594 GList *
1595 gaim_plugins_get_all(void)
1596 {
1597 return plugins;
1598 }
1599
1600
1601 GaimPluginAction *
1602 gaim_plugin_action_new(const char* label, void (*callback)(GaimPluginAction *))
1603 {
1604 GaimPluginAction *act = g_new0(GaimPluginAction, 1);
1605
1606 act->label = g_strdup(label);
1607 act->callback = callback;
1608
1609 return act;
1610 }
1611
1612 void
1613 gaim_plugin_action_free(GaimPluginAction *action)
1614 {
1615 g_return_if_fail(action != NULL);
1616
1617 g_free(action->label);
1618 g_free(action);
1619 }