comparison src/plugin.c @ 5205:fefad67de2c7

[gaim-migrate @ 5573] I had a damn good commit message, but it was eaten. Let's try it again. Announcing, Gaim Plugin API version 2.0, or GPAPIV2.0 for short. There are lots'a cool thingies here. Okay now, this isn't as cool as the previous message, but: 1) There's now a single entry function for all plugin types. It returns a detailed information structure on the plugin. This removes a lot of the ugliness from old plugins. Oh yeah, libicq wasn't converted to this, so if you use it, well, you shouldn't have used it anyway, but now you can't! bwahahaha. Use AIM/ICQ. 2) There are now 3 types of plugins: Standard, Loader, and Protocol plugins. Standard plugins are, well, standard, compiled plugins. Loader plugins load other plugins. For example, the perl support is now a loader plugin. It loads perl scripts. In the future, we'll have Ruby and Python loader plugins. Protocol plugins are, well, protocol plugins... yeah... 3) Plugins have unique IDs, so they can be referred to or automatically updated from a plugin database in the future. Neat, huh? 4) Plugins will have dependency support in the future, and can be hidden, so if you have, say, a logging core plugin, it won't have to show up, but then you load the GTK+ logging plugin and it'll auto-load the core plugin. Core/UI split plugins! 5) There will eventually be custom plugin signals and RPC of some sort, for the core/ui split plugins. So, okay, back up .gaimrc. I'd like to thank my parents for their support, javabsp for helping convert a bunch of protocol plugins, and Etan for helping convert a bunch of standard plugins. Have fun. If you have any problems, please let me know, but you probably won't have anything major happen. You will have to convert your plugins, though, and I'm not guaranteeing that all perl scripts will still work. I'll end up changing the perl script API eventually, so I know they won't down the road. Don't worry, though. It'll be mass cool. faceprint wants me to just commit the damn code already. So, here we go!!! .. .. I need a massage. From a young, cute girl. Are there any young, cute girls in the audience? IM me plz k thx. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Fri, 25 Apr 2003 06:47:33 +0000
parents
children 0241d6b6702d
comparison
equal deleted inserted replaced
5204:44de70702205 5205:fefad67de2c7
1 /*
2 * gaim
3 *
4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * ----------------
23 * The Plug-in plugin
24 *
25 * Plugin support is currently being maintained by Mike Saraf
26 * msaraf@dwc.edu
27 *
28 * Well, I didn't see any work done on it for a while, so I'm going to try
29 * my hand at it. - Eric warmenhoven@yahoo.com
30 *
31 * Mike is my roomate. I can assure you that he's lazy :-P
32 * -- Rob rob@marko.net
33 *
34 * Yeah, well now I'm re-writing a good portion of it! The perl stuff was
35 * a hack. Tsk tsk! -- Christian <chipx86@gnupdate.org>
36 */
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #include "gaim.h"
43 #include "prpl.h"
44 #include "event.h"
45
46 #include <string.h>
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50
51 #include <unistd.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54
55 #ifdef _WIN32
56 #include "win32dep.h"
57 #endif
58
59 #ifdef _WIN32
60 # define PLUGIN_EXT ".dll"
61 #else
62 # define PLUGIN_EXT ".so"
63 #endif
64
65 static GList *loaded_plugins = NULL;
66 static GList *plugins = NULL;
67 static GList *plugin_loaders = NULL;
68
69 static size_t search_path_count = 0;
70 static char **search_paths = NULL;
71
72 static void (*probe_cb)(void *) = NULL;
73 static void *probe_cb_data = NULL;
74 static void (*load_cb)(GaimPlugin *, void *) = NULL;
75 static void *load_cb_data = NULL;
76 static void (*unload_cb)(GaimPlugin *, void *) = NULL;
77 static void *unload_cb_data = NULL;
78
79 #ifdef GAIM_PLUGINS
80 static int
81 is_so_file(const char *filename, const char *ext)
82 {
83 int len, extlen;
84
85 if (filename == NULL || *filename == '\0' || ext == NULL)
86 return 0;
87
88 extlen = strlen(ext);
89 len = strlen(filename) - extlen;
90
91 if (len < 0)
92 return 0;
93
94 return (!strncmp(filename + len, ext, extlen));
95 }
96
97 static gboolean
98 __loader_supports_file(GaimPlugin *loader, const char *filename)
99 {
100 GList *l, *exts;
101 GaimPlugin *plugin;
102
103 for (l = plugin_loaders; l != NULL; l = l->next) {
104 plugin = l->data;
105
106 for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
107 exts != NULL;
108 exts = exts->next) {
109
110 if (is_so_file(filename, (char *)exts->data))
111 return TRUE;
112 }
113 }
114
115 return FALSE;
116 }
117
118 static GaimPlugin *
119 __find_loader_for_plugin(const GaimPlugin *plugin)
120 {
121 GaimPlugin *loader;
122 GList *l;
123
124 if (plugin->path == NULL)
125 return NULL;
126
127 for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) {
128 loader = l->data;
129
130 if (loader->info->type == GAIM_PLUGIN_LOADER &&
131 __loader_supports_file(loader, plugin->path)) {
132
133 return loader;
134 }
135
136 loader = NULL;
137 }
138
139 return NULL;
140 }
141
142 static gint
143 compare_prpl(GaimPlugin *a, GaimPlugin *b)
144 {
145 /* neg if a before b, 0 if equal, pos if a after b */
146 return ((GAIM_IS_PROTOCOL_PLUGIN(a)
147 ? GAIM_PLUGIN_PROTOCOL_INFO(a)->protocol : -1) -
148 ((GAIM_IS_PROTOCOL_PLUGIN(b)
149 ? GAIM_PLUGIN_PROTOCOL_INFO(b)->protocol : -1)));
150 }
151
152 #endif /* GAIM_PLUGINS */
153
154 GaimPlugin *
155 gaim_plugin_new(gboolean native, const char *path)
156 {
157 GaimPlugin *plugin;
158
159 plugin = g_new0(GaimPlugin, 1);
160
161 plugin->native_plugin = native;
162 plugin->path = (path == NULL ? NULL : g_strdup(path));
163
164 return plugin;
165 }
166
167 GaimPlugin *
168 gaim_plugin_probe(const char *filename)
169 {
170 #ifdef GAIM_PLUGINS
171 GaimPlugin *plugin = NULL;
172 GaimPlugin *loader;
173 gboolean (*gaim_init_plugin)(GaimPlugin *);
174
175 g_return_val_if_fail(filename != NULL, NULL);
176
177 plugin = gaim_plugins_find_with_filename(filename);
178
179 if (plugin != NULL)
180 return plugin;
181
182 plugin = gaim_plugin_new(is_so_file(filename, PLUGIN_EXT), filename);
183
184 if (plugin->native_plugin) {
185 plugin->handle = g_module_open(filename, 0);
186
187 if (plugin->handle == NULL) {
188 plugin->error = g_strdup(g_module_error());
189
190 return plugin;
191 }
192
193 if (!g_module_symbol(plugin->handle, "gaim_init_plugin",
194 (gpointer *)&gaim_init_plugin)) {
195 g_module_close(plugin->handle);
196 plugin->handle = NULL;
197
198 debug_printf("%s is unloadable %s\n",
199 plugin->path, g_module_error());
200
201 gaim_plugin_destroy(plugin);
202
203 return NULL;
204 }
205 }
206 else {
207 loader = __find_loader_for_plugin(plugin);
208
209 if (loader == NULL) {
210 gaim_plugin_destroy(plugin);
211
212 return NULL;
213 }
214
215 gaim_init_plugin = GAIM_PLUGIN_LOADER_INFO(loader)->probe;
216 }
217
218 plugin->error = NULL;
219
220 if (!gaim_init_plugin(plugin) || plugin->info == NULL) {
221 char buf[BUFSIZ];
222
223 g_snprintf(buf, sizeof(buf),
224 _("The plugin %s did not return any valid plugin "
225 "information"),
226 plugin->path);
227
228 do_error_dialog(_("Gaim was unable to load your plugin."), buf,
229 GAIM_ERROR);
230
231 gaim_plugin_destroy(plugin);
232
233 return NULL;
234 }
235
236 return plugin;
237 #else
238 return NULL;
239 #endif /* !GAIM_PLUGINS */
240 }
241
242 gboolean
243 gaim_plugin_load(GaimPlugin *plugin)
244 {
245 #ifdef GAIM_PLUGINS
246 g_return_val_if_fail(plugin != NULL, FALSE);
247
248 if (gaim_plugin_is_loaded(plugin))
249 return TRUE;
250
251 if (plugin->native_plugin) {
252 if (plugin->info != NULL && plugin->info->load != NULL)
253 plugin->info->load(plugin);
254 }
255 else {
256 GaimPlugin *loader;
257 GaimPluginLoaderInfo *loader_info;
258
259 loader = __find_loader_for_plugin(plugin);
260
261 if (loader == NULL)
262 return FALSE;
263
264 loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
265
266 if (loader_info->load != NULL)
267 loader_info->load(plugin);
268 }
269
270 loaded_plugins = g_list_append(loaded_plugins, plugin);
271
272 plugin->loaded = TRUE;
273
274 /* TODO */
275 if (load_cb != NULL)
276 load_cb(plugin, load_cb_data);
277
278 return TRUE;
279
280 #else
281 return FALSE;
282 #endif /* !GAIM_PLUGINS */
283 }
284
285 gboolean
286 gaim_plugin_unload(GaimPlugin *plugin)
287 {
288 #ifdef GAIM_PLUGINS
289 g_return_val_if_fail(plugin != NULL, FALSE);
290
291 loaded_plugins = g_list_remove(loaded_plugins, plugin);
292
293 g_return_val_if_fail(gaim_plugin_is_loaded(plugin), FALSE);
294
295 debug_printf("Unloading plugin %s\n", plugin->info->name);
296
297 /* cancel any pending dialogs the plugin has */
298 do_ask_cancel_by_handle(plugin);
299
300 plugin->loaded = FALSE;
301
302 if (plugin->native_plugin) {
303 if (plugin->info->unload != NULL)
304 plugin->info->unload(plugin);
305
306 if (plugin->info->type == GAIM_PLUGIN_PROTOCOL) {
307 GaimPluginProtocolInfo *prpl_info;
308 GList *l;
309 struct proto_user_split *pus;
310 struct proto_user_opt *puo;
311
312 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
313
314 for (l = prpl_info->user_splits; l != NULL; l = l->next) {
315 pus = l->data;
316
317 g_free(pus->label);
318 g_free(pus->def);
319 g_free(pus);
320 }
321
322 g_list_free(prpl_info->user_splits);
323
324 for (l = prpl_info->user_opts; l != NULL; l = l->next) {
325 puo = l->data;
326
327 g_free(puo->label);
328 g_free(puo->def);
329 g_free(puo);
330 }
331
332 g_list_free(prpl_info->user_opts);
333 }
334 }
335 else {
336 GaimPlugin *loader;
337 GaimPluginLoaderInfo *loader_info;
338
339 loader = __find_loader_for_plugin(plugin);
340
341 if (loader == NULL)
342 return FALSE;
343
344 loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
345
346 if (loader_info->load != NULL)
347 loader_info->unload(plugin);
348 }
349
350 gaim_signals_disconnect_by_handle(plugin);
351
352 /* TODO */
353 if (unload_cb != NULL)
354 unload_cb(plugin, unload_cb_data);
355
356 return TRUE;
357 #endif /* GAIM_PLUGINS */
358 }
359
360 gboolean
361 gaim_plugin_reload(GaimPlugin *plugin)
362 {
363 #ifdef GAIM_PLUGINS
364 g_return_val_if_fail(plugin != NULL, FALSE);
365 g_return_val_if_fail(gaim_plugin_is_loaded(plugin), FALSE);
366
367 if (!gaim_plugin_unload(plugin))
368 return FALSE;
369
370 if (!gaim_plugin_load(plugin))
371 return FALSE;
372
373 return TRUE;
374 #else
375 return NULL;
376 #endif /* !GAIM_PLUGINS */
377 }
378
379 void
380 gaim_plugin_destroy(GaimPlugin *plugin)
381 {
382 g_return_if_fail(plugin != NULL);
383
384 if (gaim_plugin_is_loaded(plugin))
385 gaim_plugin_unload(plugin);
386
387 plugins = g_list_remove(plugins, plugin);
388
389 /* XXX */
390 if (plugin->info != NULL && plugin->info->type == GAIM_PLUGIN_PROTOCOL)
391 return;
392
393 if (plugin->native_plugin) {
394
395 if (plugin->info != NULL && plugin->info->type == GAIM_PLUGIN_LOADER) {
396 GList *exts, *l, *next_l;
397 GaimPlugin *p2;
398
399 for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
400 exts != NULL;
401 exts = exts->next) {
402
403 for (l = gaim_plugins_get_all(); l != NULL; l = next_l) {
404 next_l = l->next;
405
406 p2 = l->data;
407
408 if (p2->path != NULL && is_so_file(p2->path, exts->data))
409 gaim_plugin_destroy(p2);
410 }
411 }
412
413 g_list_free(GAIM_PLUGIN_LOADER_INFO(plugin)->exts);
414
415 plugin_loaders = g_list_remove(plugin_loaders, plugin);
416 }
417
418 if (plugin->info != NULL && plugin->info->destroy != NULL)
419 plugin->info->destroy(plugin);
420
421 if (plugin->handle != NULL)
422 g_module_close(plugin->handle);
423 }
424 else {
425 GaimPlugin *loader;
426 GaimPluginLoaderInfo *loader_info;
427
428 loader = __find_loader_for_plugin(plugin);
429
430 if (loader == NULL)
431 return;
432
433 loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
434
435 if (loader_info->destroy != NULL)
436 loader_info->destroy(plugin);
437 }
438
439 if (plugin->info != NULL && plugin->info->dependencies != NULL)
440 g_list_free(plugin->info->dependencies);
441
442 if (plugin->path != NULL) g_free(plugin->path);
443 if (plugin->error != NULL) g_free(plugin->error);
444
445 g_free(plugin);
446 }
447
448 gboolean
449 gaim_plugin_is_loaded(const GaimPlugin *plugin)
450 {
451 g_return_val_if_fail(plugin != NULL, FALSE);
452
453 return plugin->loaded;
454 }
455
456 void
457 gaim_plugins_set_search_paths(size_t count, char **paths)
458 {
459 size_t s;
460
461 g_return_if_fail(count > 0);
462 g_return_if_fail(paths != NULL);
463
464 if (search_paths != NULL) {
465 for (s = 0; s < search_path_count; s++)
466 g_free(search_paths[s]);
467
468 g_free(search_paths);
469 }
470
471 search_paths = g_new0(char *, count);
472
473 for (s = 0; s < count; s++) {
474 if (paths[s] == NULL)
475 search_paths[s] = NULL;
476 else
477 search_paths[s] = g_strdup(paths[s]);
478 }
479
480 search_path_count = count;
481 }
482
483 void
484 gaim_plugins_unload_all(void)
485 {
486 #ifdef GAIM_PLUGINS
487
488 while (loaded_plugins != NULL)
489 gaim_plugin_unload(loaded_plugins->data);
490
491 #endif /* GAIM_PLUGINS */
492 }
493
494 void
495 gaim_plugins_probe(const char *ext)
496 {
497 #ifdef GAIM_PLUGINS
498 GDir *dir;
499 const gchar *file;
500 gchar *path;
501 GaimPlugin *plugin;
502 size_t i;
503
504 if (!g_module_supported())
505 return;
506
507 for (i = 0; i < search_path_count; i++) {
508 if (search_paths[i] == NULL)
509 continue;
510
511 dir = g_dir_open(search_paths[i], 0, NULL);
512
513 if (dir != NULL) {
514 while ((file = g_dir_read_name(dir)) != NULL) {
515 path = g_build_filename(search_paths[i], file, NULL);
516
517 if (ext == NULL || is_so_file(file, ext))
518 plugin = gaim_plugin_probe(path);
519
520 g_free(path);
521 }
522
523 g_dir_close(dir);
524 }
525 }
526
527 if (probe_cb != NULL)
528 probe_cb(probe_cb_data);
529
530 #endif /* GAIM_PLUGINS */
531 }
532
533 gboolean
534 gaim_plugin_register(GaimPlugin *plugin)
535 {
536 #ifdef GAIM_PLUGINS
537 g_return_val_if_fail(plugin != NULL, FALSE);
538
539 if (g_list_find(plugins, plugin))
540 return TRUE;
541
542 /* Special exception for loader plugins. We want them loaded NOW! */
543 if (plugin->info->type == GAIM_PLUGIN_LOADER) {
544 GList *exts;
545
546 /* We'll just load this right now. */
547 if (!gaim_plugin_load(plugin)) {
548
549 gaim_plugin_destroy(plugin);
550
551 return FALSE;
552 }
553
554 plugin_loaders = g_list_append(plugin_loaders, plugin);
555
556 for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
557 exts != NULL;
558 exts = exts->next) {
559
560 gaim_plugins_probe(exts->data);
561 }
562 }
563 else if (plugin->info->type == GAIM_PLUGIN_PROTOCOL) {
564
565 /* We'll just load this right now. */
566 if (!gaim_plugin_load(plugin)) {
567
568 gaim_plugin_destroy(plugin);
569
570 return FALSE;
571 }
572
573 if (GAIM_PLUGIN_PROTOCOL_INFO(plugin)->protocol == GAIM_PROTO_ICQ ||
574 gaim_find_prpl(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->protocol)) {
575
576 /* Nothing to see here--move along, move along */
577 gaim_plugin_destroy(plugin);
578
579 return FALSE;
580 }
581
582 protocols = g_slist_insert_sorted(protocols, plugin,
583 (GCompareFunc)compare_prpl);
584 }
585
586 plugins = g_list_append(plugins, plugin);
587
588 return TRUE;
589 #else
590 return FALSE;
591 #endif /* !GAIM_PLUGINS */
592 }
593
594 gboolean
595 gaim_plugins_enabled(void)
596 {
597 #ifdef GAIM_PLUGINS
598 return TRUE;
599 #else
600 return FALSE;
601 #endif
602 }
603
604 void
605 gaim_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
606 {
607 /* TODO */
608 probe_cb = func;
609 probe_cb_data = data;
610 }
611
612 void
613 gaim_plugins_unregister_probe_notify_cb(void (*func)(void *))
614 {
615 /* TODO */
616 probe_cb = NULL;
617 probe_cb_data = NULL;
618 }
619
620 void
621 gaim_plugins_register_load_notify_cb(void (*func)(GaimPlugin *, void *),
622 void *data)
623 {
624 /* TODO */
625 load_cb = func;
626 load_cb_data = data;
627 }
628
629 void
630 gaim_plugins_unregister_load_notify_cb(void (*func)(GaimPlugin *, void *))
631 {
632 /* TODO */
633 load_cb = NULL;
634 load_cb_data = NULL;
635 }
636
637 void
638 gaim_plugins_register_unload_notify_cb(void (*func)(GaimPlugin *, void *),
639 void *data)
640 {
641 /* TODO */
642 unload_cb = func;
643 unload_cb_data = data;
644 }
645
646 void
647 gaim_plugins_unregister_unload_notify_cb(void (*func)(GaimPlugin *, void *))
648 {
649 /* TODO */
650 unload_cb = NULL;
651 unload_cb_data = NULL;
652 }
653
654 GaimPlugin *
655 gaim_plugins_find_with_name(const char *name)
656 {
657 GaimPlugin *plugin;
658 GList *l;
659
660 for (l = plugins; l != NULL; l = l->next) {
661 plugin = l->data;
662
663 if (!strcmp(plugin->info->name, name))
664 return plugin;
665 }
666
667 return NULL;
668 }
669
670 GaimPlugin *
671 gaim_plugins_find_with_filename(const char *filename)
672 {
673 GaimPlugin *plugin;
674 GList *l;
675
676 for (l = plugins; l != NULL; l = l->next) {
677 plugin = l->data;
678
679 if (plugin->path != NULL && !strcmp(plugin->path, filename))
680 return plugin;
681 }
682
683 return NULL;
684 }
685
686 GaimPlugin *
687 gaim_plugins_find_with_id(const char *id)
688 {
689 GaimPlugin *plugin;
690 GList *l;
691
692 g_return_val_if_fail(id != NULL, NULL);
693
694 for (l = plugins; l != NULL; l = l->next) {
695 plugin = l->data;
696
697 if (!strcmp(plugin->info->id, id))
698 return plugin;
699 }
700
701 return NULL;
702 }
703
704 GList *
705 gaim_plugins_get_loaded(void)
706 {
707 return loaded_plugins;
708 }
709
710 GList *
711 gaim_plugins_get_all(void)
712 {
713 return plugins;
714 }
715