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