The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / libpurple / plugin.c~ @ a274da4f

History | View | Annotate | Download (39.1 KB)

1
/*
2
 * purple
3
 *
4
 * Purple 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
21
 */
22
#define _PURPLE_PLUGIN_C_
23

    
24
#include "internal.h"
25

    
26
#include "accountopt.h"
27
#include "core.h"
28
#include "dbus-maybe.h"
29
#include "debug.h"
30
#include "notify.h"
31
#include "prefs.h"
32
#include "prpl.h"
33
#include "request.h"
34
#include "signals.h"
35
#include "util.h"
36
#include "valgrind.h"
37
#include "version.h"
38

    
39
typedef struct
40
{
41
	GHashTable *commands;
42
	size_t command_count;
43

    
44
} PurplePluginIpcInfo;
45

    
46
typedef struct
47
{
48
	PurpleCallback func;
49
	PurpleSignalMarshalFunc marshal;
50

    
51
	int num_params;
52
	PurpleValue **params;
53
	PurpleValue *ret_value;
54

    
55
} PurplePluginIpcCommand;
56

    
57
static GList *search_paths     = NULL;
58
static GList *plugins          = NULL;
59
static GList *loaded_plugins   = NULL;
60
static GList *protocol_plugins = NULL;
61
#ifdef PURPLE_PLUGINS
62
static GList *load_queue       = NULL;
63
static GList *plugin_loaders   = NULL;
64
static GList *plugins_to_disable = NULL;
65
#endif
66

    
67
static void (*probe_cb)(void *) = NULL;
68
static void *probe_cb_data = NULL;
69
static void (*load_cb)(PurplePlugin *, void *) = NULL;
70
static void *load_cb_data = NULL;
71
static void (*unload_cb)(PurplePlugin *, void *) = NULL;
72
static void *unload_cb_data = NULL;
73

    
74
#ifdef PURPLE_PLUGINS
75

    
76
static gboolean
77
has_file_extension(const char *filename, const char *ext)
78
{
79
	int len, extlen;
80

    
81
	if (filename == NULL || *filename == '\0' || ext == NULL)
82
		return 0;
83

    
84
	extlen = strlen(ext);
85
	len = strlen(filename) - extlen;
86

    
87
	if (len < 0)
88
		return 0;
89

    
90
	return (strncmp(filename + len, ext, extlen) == 0);
91
}
92

    
93
static gboolean
94
is_native(const char *filename)
95
{
96
	const char *last_period;
97

    
98
	last_period = strrchr(filename, '.');
99
	if (last_period == NULL)
100
		return FALSE;
101

    
102
	return !(strcmp(last_period, ".dll") &
103
			 strcmp(last_period, ".sl") &
104
			 strcmp(last_period, ".so"));
105
}
106

    
107
static char *
108
purple_plugin_get_basename(const char *filename)
109
{
110
	const char *basename;
111
	const char *last_period;
112

    
113
	basename = strrchr(filename, G_DIR_SEPARATOR);
114
	if (basename != NULL)
115
		basename++;
116
	else
117
		basename = filename;
118

    
119
	if (is_native(basename) &&
120
		((last_period = strrchr(basename, '.')) != NULL))
121
			return g_strndup(basename, (last_period - basename));
122

    
123
	return g_strdup(basename);
124
}
125

    
126
static gboolean
127
loader_supports_file(PurplePlugin *loader, const char *filename)
128
{
129
	GList *exts;
130

    
131
	for (exts = PURPLE_PLUGIN_LOADER_INFO(loader)->exts; exts != NULL; exts = exts->next) {
132
		if (has_file_extension(filename, (char *)exts->data)) {
133
			return TRUE;
134
		}
135
	}
136

    
137
	return FALSE;
138
}
139

    
140
static PurplePlugin *
141
find_loader_for_plugin(const PurplePlugin *plugin)
142
{
143
	PurplePlugin *loader;
144
	GList *l;
145

    
146
	if (plugin->path == NULL)
147
		return NULL;
148

    
149
	for (l = purple_plugins_get_loaded(); l != NULL; l = l->next) {
150
		loader = l->data;
151

    
152
		if (loader->info->type == PURPLE_PLUGIN_LOADER &&
153
			loader_supports_file(loader, plugin->path)) {
154

    
155
			return loader;
156
		}
157

    
158
		loader = NULL;
159
	}
160

    
161
	return NULL;
162
}
163

    
164
#endif /* PURPLE_PLUGINS */
165

    
166
/**
167
 * Negative if a before b, 0 if equal, positive if a after b.
168
 */
169
static gint
170
compare_prpl(PurplePlugin *a, PurplePlugin *b)
171
{
172
	if(PURPLE_IS_PROTOCOL_PLUGIN(a)) {
173
		if(PURPLE_IS_PROTOCOL_PLUGIN(b))
174
			return strcmp(a->info->name, b->info->name);
175
		else
176
			return -1;
177
	} else {
178
		if(PURPLE_IS_PROTOCOL_PLUGIN(b))
179
			return 1;
180
		else
181
			return 0;
182
	}
183
}
184

    
185
PurplePlugin *
186
purple_plugin_new(gboolean native, const char *path)
187
{
188
	PurplePlugin *plugin;
189

    
190
	plugin = g_new0(PurplePlugin, 1);
191

    
192
	plugin->native_plugin = native;
193
	plugin->path = g_strdup(path);
194

    
195
	PURPLE_DBUS_REGISTER_POINTER(plugin, PurplePlugin);
196

    
197
	return plugin;
198
}
199

    
200
PurplePlugin *
201
purple_plugin_probe(const char *filename)
202
{
203
#ifdef PURPLE_PLUGINS
204
	PurplePlugin *plugin = NULL;
205
	PurplePlugin *loader;
206
	gpointer unpunned;
207
	gchar *basename = NULL;
208
	gboolean (*purple_init_plugin)(PurplePlugin *);
209

    
210
	purple_debug_misc("plugins", "probing %s\n", filename);
211
	g_return_val_if_fail(filename != NULL, NULL);
212

    
213
	if (!g_file_test(filename, G_FILE_TEST_EXISTS))
214
		return NULL;
215

    
216
	/* If this plugin has already been probed then exit */
217
	basename = purple_plugin_get_basename(filename);
218
	plugin = purple_plugins_find_with_basename(basename);
219
	g_free(basename);
220
	if (plugin != NULL)
221
	{
222
		if (purple_strequal(filename, plugin->path))
223
			return plugin;
224
		else if (!purple_plugin_is_unloadable(plugin))
225
		{
226
			purple_debug_warning("plugins", "Not loading %s. "
227
							"Another plugin with the same name (%s) has already been loaded.\n",
228
							filename, plugin->path);
229
			return plugin;
230
		}
231
		else
232
		{
233
			/* The old plugin was a different file and it was unloadable.
234
			 * There's no guarantee that this new file with the same name
235
			 * will be loadable, but unless it fails in one of the silent
236
			 * ways and the first one didn't, it's not any worse.  The user
237
			 * will still see a greyed-out plugin, which is what we want. */
238
			purple_plugin_destroy(plugin);
239
		}
240
	}
241

    
242
	plugin = purple_plugin_new(has_file_extension(filename, G_MODULE_SUFFIX), filename);
243

    
244
	if (plugin->native_plugin) {
245
		const char *error;
246
#ifdef _WIN32
247
		/* Suppress error popups for failing to load plugins */
248
		UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
249
#endif
250

    
251
		/*
252
		 * We pass G_MODULE_BIND_LOCAL here to prevent symbols from
253
		 * plugins being added to the global name space.
254
		 *
255
		 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
256
		 */
257
		plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL);
258

    
259
		if (plugin->handle == NULL)
260
		{
261
			const char *error = g_module_error();
262
			if (error != NULL && purple_str_has_prefix(error, filename))
263
			{
264
				error = error + strlen(filename);
265

    
266
				/* These are just so we don't crash.  If we
267
				 * got this far, they should always be true. */
268
				if (*error == ':')
269
					error++;
270
				if (*error == ' ')
271
					error++;
272
			}
273

    
274
			if (error == NULL || !*error)
275
			{
276
				plugin->error = g_strdup(_("Unknown error"));
277
				purple_debug_error("plugins", "%s is not loadable: Unknown error\n",
278
						 plugin->path);
279
			}
280
			else
281
			{
282
				plugin->error = g_strdup(error);
283
				purple_debug_error("plugins", "%s is not loadable: %s\n",
284
						 plugin->path, plugin->error);
285
			}
286
			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
287

    
288
			if (plugin->handle == NULL)
289
			{
290
#ifdef _WIN32
291
				/* Restore the original error mode */
292
				SetErrorMode(old_error_mode);
293
#endif
294
				purple_plugin_destroy(plugin);
295
				return NULL;
296
			}
297
			else
298
			{
299
				/* We were able to load the plugin with lazy symbol binding.
300
				 * This means we're missing some symbol.  Mark it as
301
				 * unloadable and keep going so we get the info to display
302
				 * to the user so they know to rebuild this plugin. */
303
				plugin->unloadable = TRUE;
304
			}
305
		}
306

    
307
		if (!g_module_symbol(plugin->handle, "purple_init_plugin",
308
							 &unpunned))
309
		{
310
			purple_debug_error("plugins", "%s is not usable because the "
311
							 "'purple_init_plugin' symbol could not be "
312
							 "found.  Does the plugin call the "
313
							 "PURPLE_INIT_PLUGIN() macro?\n", plugin->path);
314

    
315
			g_module_close(plugin->handle);
316
			error = g_module_error();
317
			if (error != NULL)
318
				purple_debug_error("plugins", "Error closing module %s: %s\n",
319
								 plugin->path, error);
320
			plugin->handle = NULL;
321

    
322
#ifdef _WIN32
323
			/* Restore the original error mode */
324
			SetErrorMode(old_error_mode);
325
#endif
326
			purple_plugin_destroy(plugin);
327
			return NULL;
328
		}
329
		purple_init_plugin = unpunned;
330

    
331
#ifdef _WIN32
332
		/* Restore the original error mode */
333
		SetErrorMode(old_error_mode);
334
#endif
335
	}
336
	else {
337
		loader = find_loader_for_plugin(plugin);
338

    
339
		if (loader == NULL) {
340
			purple_plugin_destroy(plugin);
341
			return NULL;
342
		}
343

    
344
		purple_init_plugin = PURPLE_PLUGIN_LOADER_INFO(loader)->probe;
345
	}
346

    
347
	if (!purple_init_plugin(plugin) || plugin->info == NULL)
348
	{
349
		purple_plugin_destroy(plugin);
350
		return NULL;
351
	}
352
	else if (plugin->info->ui_requirement &&
353
			!purple_strequal(plugin->info->ui_requirement, purple_core_get_ui()))
354
	{
355
		plugin->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
356
					purple_core_get_ui(), plugin->info->ui_requirement);
357
		purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n", plugin->path, plugin->error);
358
		plugin->unloadable = TRUE;
359
		return plugin;
360
	}
361

    
362
	/*
363
	 * Check to make sure a plugin has defined an id.
364
	 * Not having this check caused purple_plugin_unload to
365
	 * enter an infinite loop in certain situations by passing
366
	 * purple_find_plugin_by_id a NULL value. -- ecoffey
367
	 */
368
	if (plugin->info->id == NULL || *plugin->info->id == '\0')
369
	{
370
		plugin->error = g_strdup(_("This plugin has not defined an ID."));
371
		purple_debug_error("plugins", "%s is not loadable: info->id is not defined.\n", plugin->path);
372
		plugin->unloadable = TRUE;
373
		return plugin;
374
	}
375

    
376
	/* Really old plugins. */
377
	if (plugin->info->magic != PURPLE_PLUGIN_MAGIC)
378
	{
379
		if (plugin->info->magic >= 2 && plugin->info->magic <= 4)
380
		{
381
			struct _PurplePluginInfo2
382
			{
383
				unsigned int api_version;
384
				PurplePluginType type;
385
				char *ui_requirement;
386
				unsigned long flags;
387
				GList *dependencies;
388
				PurplePluginPriority priority;
389

    
390
				char *id;
391
				char *name;
392
				char *version;
393
				char *summary;
394
				char *description;
395
				char *author;
396
				char *homepage;
397

    
398
				gboolean (*load)(PurplePlugin *plugin);
399
				gboolean (*unload)(PurplePlugin *plugin);
400
				void (*destroy)(PurplePlugin *plugin);
401

    
402
				void *ui_info;
403
				void *extra_info;
404
				PurplePluginUiInfo *prefs_info;
405
				GList *(*actions)(PurplePlugin *plugin, gpointer context);
406
			} *info2 = (struct _PurplePluginInfo2 *)plugin->info;
407

    
408
			/* This leaks... but only for ancient plugins, so deal with it. */
409
			plugin->info = g_new0(PurplePluginInfo, 1);
410

    
411
			/* We don't really need all these to display the plugin info, but
412
			 * I'm copying them all for good measure. */
413
			plugin->info->magic          = info2->api_version;
414
			plugin->info->type           = info2->type;
415
			plugin->info->ui_requirement = info2->ui_requirement;
416
			plugin->info->flags          = info2->flags;
417
			plugin->info->dependencies   = info2->dependencies;
418
			plugin->info->id             = info2->id;
419
			plugin->info->name           = info2->name;
420
			plugin->info->version        = info2->version;
421
			plugin->info->summary        = info2->summary;
422
			plugin->info->description    = info2->description;
423
			plugin->info->author         = info2->author;
424
			plugin->info->homepage       = info2->homepage;
425
			plugin->info->load           = info2->load;
426
			plugin->info->unload         = info2->unload;
427
			plugin->info->destroy        = info2->destroy;
428
			plugin->info->ui_info        = info2->ui_info;
429
			plugin->info->extra_info     = info2->extra_info;
430

    
431
			if (info2->api_version >= 3)
432
				plugin->info->prefs_info = info2->prefs_info;
433

    
434
			if (info2->api_version >= 4)
435
				plugin->info->actions    = info2->actions;
436

    
437

    
438
			plugin->error = g_strdup_printf(_("Plugin magic mismatch %d (need %d)"),
439
							 plugin->info->magic, PURPLE_PLUGIN_MAGIC);
440
			purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
441
					  plugin->path, plugin->info->magic, PURPLE_PLUGIN_MAGIC);
442
			plugin->unloadable = TRUE;
443
			return plugin;
444
		}
445

    
446
		purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
447
				 plugin->path, plugin->info->magic, PURPLE_PLUGIN_MAGIC);
448
		purple_plugin_destroy(plugin);
449
		return NULL;
450
	}
451

    
452
	if (plugin->info->major_version != PURPLE_MAJOR_VERSION ||
453
			plugin->info->minor_version > PURPLE_MINOR_VERSION)
454
	{
455
		plugin->error = g_strdup_printf(_("ABI version mismatch %d.%d.x (need %d.%d.x)"),
456
						 plugin->info->major_version, plugin->info->minor_version,
457
						 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION);
458
		purple_debug_error("plugins", "%s is not loadable: ABI version mismatch %d.%d.x (need %d.%d.x)\n",
459
				 plugin->path, plugin->info->major_version, plugin->info->minor_version,
460
				 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION);
461
		plugin->unloadable = TRUE;
462
		return plugin;
463
	}
464

    
465
	if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
466
	{
467
		/* If plugin is a PRPL, make sure it implements the required functions */
468
		if ((PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon == NULL) ||
469
		    (PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->login == NULL) ||
470
		    (PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->close == NULL))
471
		{
472
			plugin->error = g_strdup(_("Plugin does not implement all required functions (list_icon, login and close)"));
473
			purple_debug_error("plugins", "%s is not loadable: %s\n",
474
					 plugin->path, plugin->error);
475
			plugin->unloadable = TRUE;
476
			return plugin;
477
		}
478

    
479
		/* For debugging, let's warn about prpl prefs. */
480
		if (plugin->info->prefs_info != NULL)
481
		{
482
			purple_debug_error("plugins", "%s has a prefs_info, but is a prpl. This is no longer supported.\n",
483
			                 plugin->path);
484
		}
485
	}
486

    
487
	return plugin;
488
#else
489
	return NULL;
490
#endif /* !PURPLE_PLUGINS */
491
}
492

    
493
#ifdef PURPLE_PLUGINS
494
static gint
495
compare_plugins(gconstpointer a, gconstpointer b)
496
{
497
	const PurplePlugin *plugina = a;
498
	const PurplePlugin *pluginb = b;
499

    
500
	return strcmp(plugina->info->name, pluginb->info->name);
501
}
502
#endif /* PURPLE_PLUGINS */
503

    
504
gboolean
505
purple_plugin_load(PurplePlugin *plugin)
506
{
507
#ifdef PURPLE_PLUGINS
508
	GList *dep_list = NULL;
509
	GList *l;
510

    
511
	g_return_val_if_fail(plugin != NULL, FALSE);
512

    
513
	if (purple_plugin_is_loaded(plugin))
514
		return TRUE;
515

    
516
	if (purple_plugin_is_unloadable(plugin))
517
		return FALSE;
518

    
519
	g_return_val_if_fail(plugin->error == NULL, FALSE);
520

    
521
	/*
522
	 * Go through the list of the plugin's dependencies.
523
	 *
524
	 * First pass: Make sure all the plugins needed are probed.
525
	 */
526
	for (l = plugin->info->dependencies; l != NULL; l = l->next)
527
	{
528
		const char *dep_name = (const char *)l->data;
529
		PurplePlugin *dep_plugin;
530

    
531
		dep_plugin = purple_plugins_find_with_id(dep_name);
532

    
533
		if (dep_plugin == NULL)
534
		{
535
			char *tmp;
536

    
537
			tmp = g_strdup_printf(_("The required plugin %s was not found. "
538
			                        "Please install this plugin and try again."),
539
			                      dep_name);
540

    
541
			purple_notify_error(NULL, NULL,
542
			                  _("Unable to load the plugin"), tmp);
543
			g_free(tmp);
544

    
545
			g_list_free(dep_list);
546

    
547
			return FALSE;
548
		}
549

    
550
		dep_list = g_list_append(dep_list, dep_plugin);
551
	}
552

    
553
	/* Second pass: load all the required plugins. */
554
	for (l = dep_list; l != NULL; l = l->next)
555
	{
556
		PurplePlugin *dep_plugin = (PurplePlugin *)l->data;
557

    
558
		if (!purple_plugin_is_loaded(dep_plugin))
559
		{
560
			if (!purple_plugin_load(dep_plugin))
561
			{
562
				char *tmp;
563

    
564
				tmp = g_strdup_printf(_("The required plugin %s was unable to load."),
565
				                      plugin->info->name);
566

    
567
				purple_notify_error(NULL, NULL,
568
				                 _("Unable to load your plugin."), tmp);
569
				g_free(tmp);
570

    
571
				g_list_free(dep_list);
572

    
573
				return FALSE;
574
			}
575
		}
576
	}
577

    
578
	/* Third pass: note that other plugins are dependencies of this plugin.
579
	 * This is done separately in case we had to bail out earlier. */
580
	for (l = dep_list; l != NULL; l = l->next)
581
	{
582
		PurplePlugin *dep_plugin = (PurplePlugin *)l->data;
583
		dep_plugin->dependent_plugins = g_list_prepend(dep_plugin->dependent_plugins, plugin->info->id);
584
	}
585

    
586
	g_list_free(dep_list);
587

    
588
	if (plugin->native_plugin)
589
	{
590
		if (plugin->info != NULL && plugin->info->load != NULL)
591
		{
592
			if (!plugin->info->load(plugin))
593
				return FALSE;
594
		}
595
	}
596
	else {
597
		PurplePlugin *loader;
598
		PurplePluginLoaderInfo *loader_info;
599

    
600
		loader = find_loader_for_plugin(plugin);
601

    
602
		if (loader == NULL)
603
			return FALSE;
604

    
605
		loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
606

    
607
		if (loader_info->load != NULL)
608
		{
609
			if (!loader_info->load(plugin))
610
				return FALSE;
611
		}
612
	}
613

    
614
	loaded_plugins = g_list_insert_sorted(loaded_plugins, plugin, compare_plugins);
615

    
616
	plugin->loaded = TRUE;
617

    
618
	if (load_cb != NULL)
619
		load_cb(plugin, load_cb_data);
620

    
621
	purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
622

    
623
	return TRUE;
624

    
625
#else
626
	return TRUE;
627
#endif /* !PURPLE_PLUGINS */
628
}
629

    
630
gboolean
631
purple_plugin_unload(PurplePlugin *plugin)
632
{
633
#ifdef PURPLE_PLUGINS
634
	GList *l;
635
	GList *ll;
636

    
637
	g_return_val_if_fail(plugin != NULL, FALSE);
638
	g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE);
639

    
640
	purple_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name);
641

    
642
	/* Unload all plugins that depend on this plugin. */
643
	for (l = plugin->dependent_plugins; l != NULL; l = ll) {
644
		const char * dep_name = (const char *)l->data;
645
		PurplePlugin *dep_plugin;
646

    
647
		/* Store a pointer to the next element in the list.
648
		 * This is because we'll be modifying this list in the loop. */
649
		ll = l->next;
650

    
651
		dep_plugin = purple_plugins_find_with_id(dep_name);
652

    
653
		if (dep_plugin != NULL && purple_plugin_is_loaded(dep_plugin))
654
		{
655
			if (!purple_plugin_unload(dep_plugin))
656
			{
657
				g_free(plugin->error);
658
				plugin->error = g_strdup_printf(_("%s requires %s, but it failed to unload."),
659
				                                _(plugin->info->name),
660
				                                _(dep_plugin->info->name));
661
				return FALSE;
662
			}
663
			else
664
			{
665
#if 0
666
				/* This isn't necessary. This has already been done when unloading dep_plugin. */
667
				plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l);
668
#endif
669
			}
670
		}
671
	}
672

    
673
	/* Remove this plugin from each dependency's dependent_plugins list. */
674
	for (l = plugin->info->dependencies; l != NULL; l = l->next)
675
	{
676
		const char *dep_name = (const char *)l->data;
677
		PurplePlugin *dependency;
678

    
679
		dependency = purple_plugins_find_with_id(dep_name);
680
		
681

    
682
		if (dependency != NULL)
683
			dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id);
684
		else
685
			purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name);
686
	}
687

    
688
	if (plugin->native_plugin) {
689
		if (plugin->info->unload && !plugin->info->unload(plugin))
690
			return FALSE;
691

    
692
		if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) {
693
			PurplePluginProtocolInfo *prpl_info;
694
			GList *l;
695

    
696
			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
697

    
698
			for (l = prpl_info->user_splits; l != NULL; l = l->next)
699
				purple_account_user_split_destroy(l->data);
700

    
701
			for (l = prpl_info->protocol_options; l != NULL; l = l->next)
702
				purple_account_option_destroy(l->data);
703

    
704
			if (prpl_info->user_splits != NULL) {
705
				g_list_free(prpl_info->user_splits);
706
				prpl_info->user_splits = NULL;
707
			}
708

    
709
			if (prpl_info->protocol_options != NULL) {
710
				g_list_free(prpl_info->protocol_options);
711
				prpl_info->protocol_options = NULL;
712
			}
713
		}
714
	} else {
715
		PurplePlugin *loader;
716
		PurplePluginLoaderInfo *loader_info;
717

    
718
		loader = find_loader_for_plugin(plugin);
719

    
720
		if (loader == NULL)
721
			return FALSE;
722

    
723
		loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
724

    
725
		if (loader_info->unload && !loader_info->unload(plugin))
726
			return FALSE;
727
	}
728
	
729

    
730
	
731
	/* cancel any pending dialogs the plugin has */
732
	purple_request_close_with_handle(plugin);
733
	purple_notify_close_with_handle(plugin);
734

    
735
	purple_signals_disconnect_by_handle(plugin);
736
	purple_plugin_ipc_unregister_all(plugin);
737

    
738
	loaded_plugins = g_list_remove(loaded_plugins, plugin);
739
	if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin)) 
740
		protocol_plugins = g_list_remove(protocol_plugins, plugin);
741

    
742
	plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
743
	plugin->loaded = FALSE;
744

    
745
	/* We wouldn't be anywhere near here if the plugin wasn't loaded, so
746
	 * if plugin->error is set at all, it had to be from a previous
747
	 * unload failure.  It's obviously okay now.
748
	 */
749
	g_free(plugin->error);
750
	plugin->error = NULL;
751

    
752
	if (unload_cb != NULL)
753
		unload_cb(plugin, unload_cb_data);
754

    
755
	purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
756

    
757
	purple_prefs_disconnect_by_handle(plugin);
758

    
759
	return TRUE;
760
#else
761
	return TRUE;
762
#endif /* PURPLE_PLUGINS */
763
}
764

    
765
void
766
purple_plugin_disable(PurplePlugin *plugin)
767
{
768
#ifdef PURPLE_PLUGINS
769
	g_return_if_fail(plugin != NULL);
770

    
771
	if (!g_list_find(plugins_to_disable, plugin))
772
		plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
773
#endif
774
}
775

    
776
gboolean
777
purple_plugin_reload(PurplePlugin *plugin)
778
{
779
#ifdef PURPLE_PLUGINS
780
	g_return_val_if_fail(plugin != NULL, FALSE);
781
	g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE);
782

    
783
	if (!purple_plugin_unload(plugin))
784
		return FALSE;
785

    
786
	if (!purple_plugin_load(plugin))
787
		return FALSE;
788

    
789
	return TRUE;
790
#else
791
	return TRUE;
792
#endif /* !PURPLE_PLUGINS */
793
}
794

    
795
void
796
purple_plugin_destroy(PurplePlugin *plugin)
797
{
798
#ifdef PURPLE_PLUGINS
799
	g_return_if_fail(plugin != NULL);
800

    
801
	if (purple_plugin_is_loaded(plugin))
802
		purple_plugin_unload(plugin);
803

    
804
	plugins = g_list_remove(plugins, plugin);
805

    
806
	if (load_queue != NULL)
807
		load_queue = g_list_remove(load_queue, plugin);
808

    
809
	/* true, this may leak a little memory if there is a major version
810
	 * mismatch, but it's a lot better than trying to free something
811
	 * we shouldn't, and crashing while trying to load an old plugin */
812
	if(plugin->info == NULL || plugin->info->magic != PURPLE_PLUGIN_MAGIC ||
813
			plugin->info->major_version != PURPLE_MAJOR_VERSION)
814
	{
815
		if(plugin->handle)
816
			g_module_close(plugin->handle);
817

    
818
		g_free(plugin->path);
819
		g_free(plugin->error);
820

    
821
		PURPLE_DBUS_UNREGISTER_POINTER(plugin);
822

    
823
		g_free(plugin);
824
		return;
825
	}
826

    
827
	if (plugin->info != NULL)
828
		g_list_free(plugin->info->dependencies);
829

    
830
	if (plugin->native_plugin)
831
	{
832
		if (plugin->info != NULL && plugin->info->type == PURPLE_PLUGIN_LOADER)
833
		{
834
			PurplePluginLoaderInfo *loader_info;
835
			GList *exts, *l, *next_l;
836
			PurplePlugin *p2;
837

    
838
			loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin);
839

    
840
			if (loader_info != NULL && loader_info->exts != NULL)
841
			{
842
				for (exts = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts;
843
					 exts != NULL;
844
					 exts = exts->next) {
845

    
846
					for (l = purple_plugins_get_all(); l != NULL; l = next_l)
847
					{
848
						next_l = l->next;
849

    
850
						p2 = l->data;
851

    
852
						if (p2->path != NULL &&
853
							has_file_extension(p2->path, exts->data))
854
						{
855
							purple_plugin_destroy(p2);
856
						}
857
					}
858
				}
859

    
860
				g_list_free(loader_info->exts);
861
				loader_info->exts = NULL;
862
			}
863

    
864
			plugin_loaders = g_list_remove(plugin_loaders, plugin);
865
		}
866

    
867
		if (plugin->info != NULL && plugin->info->destroy != NULL)
868
			plugin->info->destroy(plugin);
869

    
870
		/*
871
		 * I find it extremely useful to do this when using valgrind, as
872
		 * it keeps all the plugins open, meaning that valgrind is able to
873
		 * resolve symbol names in leak traces from plugins.
874
		 */
875
		if (!g_getenv("PURPLE_LEAKCHECK_HELP") && !RUNNING_ON_VALGRIND)
876
		{
877
			if (plugin->handle != NULL)
878
				g_module_close(plugin->handle);
879
		}
880
	}
881
	else
882
	{
883
		PurplePlugin *loader;
884
		PurplePluginLoaderInfo *loader_info;
885

    
886
		loader = find_loader_for_plugin(plugin);
887

    
888
		if (loader != NULL)
889
		{
890
			loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
891

    
892
			if (loader_info->destroy != NULL)
893
				loader_info->destroy(plugin);
894
		}
895
	}
896

    
897
	g_free(plugin->path);
898
	g_free(plugin->error);
899

    
900
	PURPLE_DBUS_UNREGISTER_POINTER(plugin);
901

    
902
	g_free(plugin);
903
#endif /* !PURPLE_PLUGINS */
904
}
905

    
906
gboolean
907
purple_plugin_is_loaded(const PurplePlugin *plugin)
908
{
909
	g_return_val_if_fail(plugin != NULL, FALSE);
910

    
911
	return plugin->loaded;
912
}
913

    
914
gboolean
915
purple_plugin_is_unloadable(const PurplePlugin *plugin)
916
{
917
	g_return_val_if_fail(plugin != NULL, FALSE);
918

    
919
	return plugin->unloadable;
920
}
921

    
922
const gchar *
923
purple_plugin_get_id(const PurplePlugin *plugin) {
924
	g_return_val_if_fail(plugin, NULL);
925
	g_return_val_if_fail(plugin->info, NULL);
926

    
927
	return plugin->info->id;
928
}
929

    
930
const gchar *
931
purple_plugin_get_name(const PurplePlugin *plugin) {
932
	g_return_val_if_fail(plugin, NULL);
933
	g_return_val_if_fail(plugin->info, NULL);
934

    
935
	return _(plugin->info->name);
936
}
937

    
938
const gchar *
939
purple_plugin_get_version(const PurplePlugin *plugin) {
940
	g_return_val_if_fail(plugin, NULL);
941
	g_return_val_if_fail(plugin->info, NULL);
942

    
943
	return plugin->info->version;
944
}
945

    
946
const gchar *
947
purple_plugin_get_summary(const PurplePlugin *plugin) {
948
	g_return_val_if_fail(plugin, NULL);
949
	g_return_val_if_fail(plugin->info, NULL);
950

    
951
	return _(plugin->info->summary);
952
}
953

    
954
const gchar *
955
purple_plugin_get_description(const PurplePlugin *plugin) {
956
	g_return_val_if_fail(plugin, NULL);
957
	g_return_val_if_fail(plugin->info, NULL);
958

    
959
	return _(plugin->info->description);
960
}
961

    
962
const gchar *
963
purple_plugin_get_author(const PurplePlugin *plugin) {
964
	g_return_val_if_fail(plugin, NULL);
965
	g_return_val_if_fail(plugin->info, NULL);
966

    
967
	return _(plugin->info->author);
968
}
969

    
970
const gchar *
971
purple_plugin_get_homepage(const PurplePlugin *plugin) {
972
	g_return_val_if_fail(plugin, NULL);
973
	g_return_val_if_fail(plugin->info, NULL);
974

    
975
	return plugin->info->homepage;
976
}
977

    
978
/**************************************************************************
979
 * Plugin IPC
980
 **************************************************************************/
981
static void
982
destroy_ipc_info(void *data)
983
{
984
	PurplePluginIpcCommand *ipc_command = (PurplePluginIpcCommand *)data;
985
	int i;
986

    
987
	if (ipc_command->params != NULL)
988
	{
989
		for (i = 0; i < ipc_command->num_params; i++)
990
			purple_value_destroy(ipc_command->params[i]);
991

    
992
		g_free(ipc_command->params);
993
	}
994

    
995
	if (ipc_command->ret_value != NULL)
996
		purple_value_destroy(ipc_command->ret_value);
997

    
998
	g_free(ipc_command);
999
}
1000

    
1001
gboolean
1002
purple_plugin_ipc_register(PurplePlugin *plugin, const char *command,
1003
						 PurpleCallback func, PurpleSignalMarshalFunc marshal,
1004
						 PurpleValue *ret_value, int num_params, ...)
1005
{
1006
	PurplePluginIpcInfo *ipc_info;
1007
	PurplePluginIpcCommand *ipc_command;
1008

    
1009
	g_return_val_if_fail(plugin  != NULL, FALSE);
1010
	g_return_val_if_fail(command != NULL, FALSE);
1011
	g_return_val_if_fail(func    != NULL, FALSE);
1012
	g_return_val_if_fail(marshal != NULL, FALSE);
1013

    
1014
	if (plugin->ipc_data == NULL)
1015
	{
1016
		ipc_info = plugin->ipc_data = g_new0(PurplePluginIpcInfo, 1);
1017
		ipc_info->commands = g_hash_table_new_full(g_str_hash, g_str_equal,
1018
												   g_free, destroy_ipc_info);
1019
	}
1020
	else
1021
		ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1022

    
1023
	ipc_command = g_new0(PurplePluginIpcCommand, 1);
1024
	ipc_command->func       = func;
1025
	ipc_command->marshal    = marshal;
1026
	ipc_command->num_params = num_params;
1027
	ipc_command->ret_value  = ret_value;
1028

    
1029
	if (num_params > 0)
1030
	{
1031
		va_list args;
1032
		int i;
1033

    
1034
		ipc_command->params = g_new0(PurpleValue *, num_params);
1035

    
1036
		va_start(args, num_params);
1037

    
1038
		for (i = 0; i < num_params; i++)
1039
			ipc_command->params[i] = va_arg(args, PurpleValue *);
1040

    
1041
		va_end(args);
1042
	}
1043

    
1044
	g_hash_table_replace(ipc_info->commands, g_strdup(command), ipc_command);
1045

    
1046
	ipc_info->command_count++;
1047

    
1048
	return TRUE;
1049
}
1050

    
1051
void
1052
purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command)
1053
{
1054
	PurplePluginIpcInfo *ipc_info;
1055

    
1056
	g_return_if_fail(plugin  != NULL);
1057
	g_return_if_fail(command != NULL);
1058

    
1059
	ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1060

    
1061
	if (ipc_info == NULL ||
1062
		g_hash_table_lookup(ipc_info->commands, command) == NULL)
1063
	{
1064
		purple_debug_error("plugins",
1065
						 "IPC command '%s' was not registered for plugin %s\n",
1066
						 command, plugin->info->name);
1067
		return;
1068
	}
1069

    
1070
	g_hash_table_remove(ipc_info->commands, command);
1071

    
1072
	ipc_info->command_count--;
1073

    
1074
	if (ipc_info->command_count == 0)
1075
	{
1076
		g_hash_table_destroy(ipc_info->commands);
1077
		g_free(ipc_info);
1078

    
1079
		plugin->ipc_data = NULL;
1080
	}
1081
}
1082

    
1083
void
1084
purple_plugin_ipc_unregister_all(PurplePlugin *plugin)
1085
{
1086
	PurplePluginIpcInfo *ipc_info;
1087

    
1088
	g_return_if_fail(plugin != NULL);
1089

    
1090
	if (plugin->ipc_data == NULL)
1091
		return; /* Silently ignore it. */
1092

    
1093
	ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1094

    
1095
	g_hash_table_destroy(ipc_info->commands);
1096
	g_free(ipc_info);
1097

    
1098
	plugin->ipc_data = NULL;
1099
}
1100

    
1101
gboolean
1102
purple_plugin_ipc_get_params(PurplePlugin *plugin, const char *command,
1103
						   PurpleValue **ret_value, int *num_params,
1104
						   PurpleValue ***params)
1105
{
1106
	PurplePluginIpcInfo *ipc_info;
1107
	PurplePluginIpcCommand *ipc_command;
1108

    
1109
	g_return_val_if_fail(plugin  != NULL, FALSE);
1110
	g_return_val_if_fail(command != NULL, FALSE);
1111

    
1112
	ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1113

    
1114
	if (ipc_info == NULL ||
1115
		(ipc_command = g_hash_table_lookup(ipc_info->commands,
1116
										   command)) == NULL)
1117
	{
1118
		purple_debug_error("plugins",
1119
						 "IPC command '%s' was not registered for plugin %s\n",
1120
						 command, plugin->info->name);
1121

    
1122
		return FALSE;
1123
	}
1124

    
1125
	if (num_params != NULL)
1126
		*num_params = ipc_command->num_params;
1127

    
1128
	if (params != NULL)
1129
		*params = ipc_command->params;
1130

    
1131
	if (ret_value != NULL)
1132
		*ret_value = ipc_command->ret_value;
1133

    
1134
	return TRUE;
1135
}
1136

    
1137
void *
1138
purple_plugin_ipc_call(PurplePlugin *plugin, const char *command,
1139
					 gboolean *ok, ...)
1140
{
1141
	PurplePluginIpcInfo *ipc_info;
1142
	PurplePluginIpcCommand *ipc_command;
1143
	va_list args;
1144
	void *ret_value;
1145

    
1146
	if (ok != NULL)
1147
		*ok = FALSE;
1148

    
1149
	g_return_val_if_fail(plugin  != NULL, NULL);
1150
	g_return_val_if_fail(command != NULL, NULL);
1151

    
1152
	ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1153

    
1154
	if (ipc_info == NULL ||
1155
		(ipc_command = g_hash_table_lookup(ipc_info->commands,
1156
										   command)) == NULL)
1157
	{
1158
		purple_debug_error("plugins",
1159
						 "IPC command '%s' was not registered for plugin %s\n",
1160
						 command, plugin->info->name);
1161

    
1162
		return NULL;
1163
	}
1164

    
1165
	va_start(args, ok);
1166
	ipc_command->marshal(ipc_command->func, args, NULL, &ret_value);
1167
	va_end(args);
1168

    
1169
	if (ok != NULL)
1170
		*ok = TRUE;
1171

    
1172
	return ret_value;
1173
}
1174

    
1175
/**************************************************************************
1176
 * Plugins subsystem
1177
 **************************************************************************/
1178
void *
1179
purple_plugins_get_handle(void) {
1180
	static int handle;
1181

    
1182
	return &handle;
1183
}
1184

    
1185
void
1186
purple_plugins_init(void) {
1187
	void *handle = purple_plugins_get_handle();
1188

    
1189
	purple_plugins_add_search_path(LIBDIR);
1190

    
1191
	purple_signal_register(handle, "plugin-load",
1192
						 purple_marshal_VOID__POINTER,
1193
						 NULL, 1,
1194
						 purple_value_new(PURPLE_TYPE_SUBTYPE,
1195
										PURPLE_SUBTYPE_PLUGIN));
1196
	purple_signal_register(handle, "plugin-unload",
1197
						 purple_marshal_VOID__POINTER,
1198
						 NULL, 1,
1199
						 purple_value_new(PURPLE_TYPE_SUBTYPE,
1200
										PURPLE_SUBTYPE_PLUGIN));
1201
}
1202

    
1203
void
1204
purple_plugins_uninit(void)
1205
{
1206
	void *handle = purple_plugins_get_handle();
1207

    
1208
	purple_signals_disconnect_by_handle(handle);
1209
	purple_signals_unregister_by_instance(handle);
1210

    
1211
	while (search_paths) {
1212
		g_free(search_paths->data);
1213
		search_paths = g_list_delete_link(search_paths, search_paths);
1214
	}
1215
}
1216

    
1217
/**************************************************************************
1218
 * Plugins API
1219
 **************************************************************************/
1220
void
1221
purple_plugins_add_search_path(const char *path)
1222
{
1223
	g_return_if_fail(path != NULL);
1224

    
1225
	if (g_list_find_custom(search_paths, path, (GCompareFunc)strcmp))
1226
		return;
1227

    
1228
	search_paths = g_list_append(search_paths, g_strdup(path));
1229
}
1230

    
1231
GList *
1232
purple_plugins_get_search_paths()
1233
{
1234
	return search_paths;
1235
}
1236

    
1237
void
1238
purple_plugins_unload_all(void)
1239
{
1240
#ifdef PURPLE_PLUGINS
1241

    
1242
	while (loaded_plugins != NULL)
1243
		purple_plugin_unload(loaded_plugins->data);
1244

    
1245
#endif /* PURPLE_PLUGINS */
1246
}
1247

    
1248
void
1249
purple_plugins_unload(PurplePluginType type)
1250
{
1251
#ifdef PURPLE_PLUGINS
1252
	GList *l;
1253

    
1254
	for (l = plugins; l; l = l->next) {
1255
		PurplePlugin *plugin = l->data;
1256
		if (plugin->info->type == type && purple_plugin_is_loaded(plugin))
1257
			purple_plugin_unload(plugin);
1258
	}
1259

    
1260
#endif /* PURPLE_PLUGINS */
1261
}
1262

    
1263
void
1264
purple_plugins_destroy_all(void)
1265
{
1266
#ifdef PURPLE_PLUGINS
1267

    
1268
	while (plugins != NULL)
1269
		purple_plugin_destroy(plugins->data);
1270

    
1271
#endif /* PURPLE_PLUGINS */
1272
}
1273

    
1274
void
1275
purple_plugins_save_loaded(const char *key)
1276
{
1277
#ifdef PURPLE_PLUGINS
1278
	GList *pl;
1279
	GList *files = NULL;
1280

    
1281
	for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
1282
		PurplePlugin *plugin = pl->data;
1283

    
1284
		if (plugin->info->type != PURPLE_PLUGIN_PROTOCOL &&
1285
		    plugin->info->type != PURPLE_PLUGIN_LOADER &&
1286
		    !g_list_find(plugins_to_disable, plugin)) {
1287
			files = g_list_append(files, plugin->path);
1288
		}
1289
	}
1290

    
1291
	purple_prefs_set_path_list(key, files);
1292
	g_list_free(files);
1293
#endif
1294
}
1295

    
1296
void
1297
purple_plugins_load_saved(const char *key)
1298
{
1299
#ifdef PURPLE_PLUGINS
1300
	GList *f, *files;
1301

    
1302
	g_return_if_fail(key != NULL);
1303

    
1304
	files = purple_prefs_get_path_list(key);
1305

    
1306
	for (f = files; f; f = f->next)
1307
	{
1308
		char *filename;
1309
		char *basename;
1310
		PurplePlugin *plugin;
1311

    
1312
		if (f->data == NULL)
1313
			continue;
1314

    
1315
		filename = f->data;
1316

    
1317
		/*
1318
		 * We don't know if the filename uses Windows or Unix path
1319
		 * separators (because people might be sharing a prefs.xml
1320
		 * file across systems), so we find the last occurrence
1321
		 * of either.
1322
		 */
1323
		basename = strrchr(filename, '/');
1324
		if ((basename == NULL) || (basename < strrchr(filename, '\\')))
1325
			basename = strrchr(filename, '\\');
1326
		if (basename != NULL)
1327
			basename++;
1328

    
1329
		/* Strip the extension */
1330
		if (basename)
1331
			basename = purple_plugin_get_basename(basename);
1332

    
1333
		if (((plugin = purple_plugins_find_with_filename(filename)) != NULL) ||
1334
				(basename && (plugin = purple_plugins_find_with_basename(basename)) != NULL) ||
1335
				((plugin = purple_plugin_probe(filename)) != NULL))
1336
		{
1337
			purple_debug_info("plugins", "Loading saved plugin %s\n",
1338
							plugin->path);
1339
			purple_plugin_load(plugin);
1340
		}
1341
		else
1342
		{
1343
			purple_debug_error("plugins", "Unable to find saved plugin %s\n",
1344
							 filename);
1345
		}
1346

    
1347
		g_free(basename);
1348

    
1349
		g_free(f->data);
1350
	}
1351

    
1352
	g_list_free(files);
1353
#endif /* PURPLE_PLUGINS */
1354
}
1355

    
1356

    
1357
void
1358
purple_plugins_probe(const char *ext)
1359
{
1360
#ifdef PURPLE_PLUGINS
1361
	
1362
	GDir *dir;
1363
	const gchar *file;
1364
	gchar *path;
1365
	PurplePlugin *plugin;
1366
	GList *cur;
1367
	const char *search_path;
1368
	
1369

    
1370
	printf("probing for plugins\n");
1371
	if (!g_module_supported())
1372
		return;
1373

    
1374
	/* Probe plugins */
1375
	for (cur = search_paths; cur != NULL; cur = cur->next)
1376
	{
1377
		search_path = cur->data;
1378

    
1379
		dir = g_dir_open(search_path, 0, NULL);
1380

    
1381
		if (dir != NULL)
1382
		{
1383
			while ((file = g_dir_read_name(dir)) != NULL)
1384
			{
1385
				path = g_build_filename(search_path, file, NULL);
1386

    
1387
				if (ext == NULL || has_file_extension(file, ext))
1388
					plugin = purple_plugin_probe(path);
1389

    
1390
				g_free(path);
1391
			}
1392

    
1393
			g_dir_close(dir);
1394
		}
1395
	}
1396

    
1397
	/* See if we have any plugins waiting to load */
1398
	while (load_queue != NULL)
1399
	{
1400
		plugin = (PurplePlugin *)load_queue->data;
1401

    
1402
		load_queue = g_list_remove(load_queue, plugin);
1403

    
1404
		if (plugin == NULL || plugin->info == NULL)
1405
			continue;
1406

    
1407
		if (plugin->info->type == PURPLE_PLUGIN_LOADER)
1408
		{
1409
			/* We'll just load this right now. */
1410
			if (!purple_plugin_load(plugin))
1411
			{
1412
				purple_plugin_destroy(plugin);
1413

    
1414
				continue;
1415
			}
1416

    
1417
			plugin_loaders = g_list_append(plugin_loaders, plugin);
1418

    
1419
			for (cur = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts;
1420
				 cur != NULL;
1421
				 cur = cur->next)
1422
			{
1423
				purple_plugins_probe(cur->data);
1424
			}
1425
		}
1426
		else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
1427
		{
1428
			/* We'll just load this right now. */
1429
			if (!purple_plugin_load(plugin))
1430
			{
1431
				purple_plugin_destroy(plugin);
1432

    
1433
				continue;
1434
			}
1435
			/* Make sure we don't load two PRPLs with the same name? */
1436
			if (purple_find_prpl(plugin->info->id))
1437
			{
1438
				/* Nothing to see here--move along, move along */
1439
				purple_plugin_destroy(plugin);
1440

    
1441
				continue;
1442
			}
1443
			
1444
			protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
1445
													(GCompareFunc)compare_prpl);
1446
			if((strncmp(plugin->info->id,"prpl-yahoojp",15)) == 0){
1447
				protocol_plugins = g_list_remove(protocol_plugins, plugin);
1448
			}
1449
		}
1450
	}
1451

    
1452
	if (probe_cb != NULL)
1453
		probe_cb(probe_cb_data);
1454

    
1455
#endif /* PURPLE_PLUGINS */
1456

    
1457
}
1458

    
1459
gboolean
1460
purple_plugin_register(PurplePlugin *plugin)
1461
{
1462
	g_return_val_if_fail(plugin != NULL, FALSE);
1463

    
1464
	/* If this plugin has been registered already then exit */
1465
	if (g_list_find(plugins, plugin))
1466
		return TRUE;
1467

    
1468
	/* Ensure the plugin has the requisite information */
1469
	if (plugin->info->type == PURPLE_PLUGIN_LOADER)
1470
	{
1471
		PurplePluginLoaderInfo *loader_info;
1472

    
1473
		loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin);
1474

    
1475
		if (loader_info == NULL)
1476
		{
1477
			purple_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n",
1478
							   plugin->path);
1479
			return FALSE;
1480
		}
1481
	}
1482
	else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
1483
	{
1484
		PurplePluginProtocolInfo *prpl_info;
1485

    
1486
		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
1487

    
1488
		if (prpl_info == NULL)
1489
		{
1490
			purple_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n",
1491
							   plugin->path);
1492
			return FALSE;
1493
		}
1494
	}
1495

    
1496
#ifdef PURPLE_PLUGINS
1497
	/* This plugin should be probed and maybe loaded--add it to the queue */
1498
	load_queue = g_list_append(load_queue, plugin);
1499
#else
1500
	if (plugin->info != NULL)
1501
	{
1502
		if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
1503
			protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
1504
													(GCompareFunc)compare_prpl);
1505
		if (plugin->info->load != NULL)
1506
			if (!plugin->info->load(plugin))
1507
				return FALSE;
1508
	}
1509
#endif
1510

    
1511
	plugins = g_list_append(plugins, plugin);
1512

    
1513
	return TRUE;
1514
}
1515

    
1516
gboolean
1517
purple_plugins_enabled(void)
1518
{
1519
#ifdef PURPLE_PLUGINS
1520
	return TRUE;
1521
#else
1522
	return FALSE;
1523
#endif
1524
}
1525

    
1526
void
1527
purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
1528
{
1529
	probe_cb = func;
1530
	probe_cb_data = data;
1531
}
1532

    
1533
void
1534
purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
1535
{
1536
	probe_cb = NULL;
1537
	probe_cb_data = NULL;
1538
}
1539

    
1540
void
1541
purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
1542
									 void *data)
1543
{
1544
	load_cb = func;
1545
	load_cb_data = data;
1546
}
1547

    
1548
void
1549
purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
1550
{
1551
	load_cb = NULL;
1552
	load_cb_data = NULL;
1553
}
1554

    
1555
void
1556
purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
1557
									   void *data)
1558
{
1559
	unload_cb = func;
1560
	unload_cb_data = data;
1561
}
1562

    
1563
void
1564
purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
1565
{
1566
	unload_cb = NULL;
1567
	unload_cb_data = NULL;
1568
}
1569

    
1570
PurplePlugin *
1571
purple_plugins_find_with_name(const char *name)
1572
{
1573
	PurplePlugin *plugin;
1574
	GList *l;
1575

    
1576
	for (l = plugins; l != NULL; l = l->next) {
1577
		plugin = l->data;
1578

    
1579
		if (purple_strequal(plugin->info->name, name))
1580
			return plugin;
1581
	}
1582

    
1583
	return NULL;
1584
}
1585

    
1586
PurplePlugin *
1587
purple_plugins_find_with_filename(const char *filename)
1588
{
1589
	PurplePlugin *plugin;
1590
	GList *l;
1591

    
1592
	for (l = plugins; l != NULL; l = l->next) {
1593
		plugin = l->data;
1594

    
1595
		if (purple_strequal(plugin->path, filename))
1596
			return plugin;
1597
	}
1598

    
1599
	return NULL;
1600
}
1601

    
1602
PurplePlugin *
1603
purple_plugins_find_with_basename(const char *basename)
1604
{
1605
#ifdef PURPLE_PLUGINS
1606
	PurplePlugin *plugin;
1607
	GList *l;
1608
	char *tmp;
1609

    
1610
	g_return_val_if_fail(basename != NULL, NULL);
1611

    
1612
	for (l = plugins; l != NULL; l = l->next)
1613
	{
1614
		plugin = (PurplePlugin *)l->data;
1615

    
1616
		if (plugin->path != NULL) {
1617
			tmp = purple_plugin_get_basename(plugin->path);
1618
			if (purple_strequal(tmp, basename))
1619
			{
1620
				g_free(tmp);
1621
				return plugin;
1622
			}
1623
			g_free(tmp);
1624
		}
1625
	}
1626

    
1627
#endif /* PURPLE_PLUGINS */
1628

    
1629
	return NULL;
1630
}
1631

    
1632
PurplePlugin *
1633
purple_plugins_find_with_id(const char *id)
1634
{
1635
	PurplePlugin *plugin;
1636
	GList *l;
1637

    
1638
	g_return_val_if_fail(id != NULL, NULL);
1639

    
1640
	for (l = plugins; l != NULL; l = l->next)
1641
	{
1642
		plugin = l->data;
1643

    
1644
		if (purple_strequal(plugin->info->id, id))
1645
			return plugin;
1646
	}
1647

    
1648
	return NULL;
1649
}
1650

    
1651
GList *
1652
purple_plugins_get_loaded(void)
1653
{
1654
	return loaded_plugins;
1655
}
1656

    
1657
GList *
1658
purple_plugins_get_protocols(void)
1659
{	
1660
	//printf("Trying to Return protocol_plugins\n");
1661
	return protocol_plugins;
1662
}
1663

    
1664
GList *
1665
purple_plugins_get_all(void)
1666
{
1667
	return plugins;
1668
}
1669

    
1670

    
1671
PurplePluginAction *
1672
purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *))
1673
{
1674
	PurplePluginAction *act = g_new0(PurplePluginAction, 1);
1675

    
1676
	act->label = g_strdup(label);
1677
	act->callback = callback;
1678

    
1679
	return act;
1680
}
1681

    
1682
void
1683
purple_plugin_action_free(PurplePluginAction *action)
1684
{
1685
	g_return_if_fail(action != NULL);
1686

    
1687
	g_free(action->label);
1688
	g_free(action);
1689
}