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
}