src/plugin.c

changeset 12695
0c93a42b7ee2
parent 12623
4f1fe7e1f808
child 12696
6c964da4a652
--- a/src/plugin.c	Tue Jan 03 11:42:51 2006 +0000
+++ b/src/plugin.c	Tue Jan 03 12:03:02 2006 +0000
@@ -28,6 +28,7 @@
 #include "prpl.h"
 #include "request.h"
 #include "signals.h"
+#include "util.h"
 #include "version.h"
 
 typedef struct
@@ -215,11 +216,24 @@
 	g_free(basename);
 	if (plugin != NULL)
 	{
-		if (strcmp(filename, plugin->path))
-			gaim_debug_info("plugins", "Not loading %s."
+		if (!strcmp(filename, plugin->path))
+			return plugin;
+		else if (!gaim_plugin_is_unloadable(plugin))
+		{
+			gaim_debug_info("plugins", "Not loading %s. "
 							"Another plugin with the same name (%s) has already been loaded.\n",
 							filename, plugin->path);
-		return plugin;
+			return plugin;
+		}
+		else
+		{
+			/* The old plugin was a different file and it was unloadable.
+			 * There's no guarantee that this new file with the same name
+			 * will be loadable, but unless it fails in one of the silent
+			 * ways and the first one didn't, it's not any worse.  The user
+			 * will still see a greyed-out plugin, which is what we want. */
+			gaim_plugin_destroy(plugin);
+		}
 	}
 
 	plugin = gaim_plugin_new(has_file_extension(filename, G_MODULE_SUFFIX), filename);
@@ -243,20 +257,50 @@
 		plugin->handle = g_module_open(filename, 0);
 #endif
 
-#ifdef _WIN32
-		/* Restore the original error mode */
-		SetErrorMode(old_error_mode);
-#endif
-
 		if (plugin->handle == NULL)
 		{
-			error = g_module_error();
-			gaim_debug_error("plugins", "%s is unloadable: %s\n",
-							 plugin->path, error ? error : "Unknown error.");
+			const char *error = g_module_error();
+			if (error == NULL)
+				error = "Unknown error";
+			else if (gaim_str_has_prefix(error, filename))
+			{
+				error = error + strlen(filename);
+
+				/* These are just so we don't crash.  If we
+				 * got this far, they should always be true. */
+				if (*error == ':')
+					error++;
+				if (*error == ' ')
+					error++;
+
+				/* This shouldn't ever be necessary. */
+				if (!*error)
+					error = "Unknown error";
+			}
+			plugin->error = g_strdup(error);
 
-			gaim_plugin_destroy(plugin);
+			gaim_debug_error("plugins", "%s is unloadable: %s\n",
+							 plugin->path, plugin->error);
+
+#if GLIB_CHECK_VERSION(2,3,3)
+			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+#else
+			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY);
+#endif
 
-			return NULL;
+			if (plugin->handle == NULL)
+			{
+				gaim_plugin_destroy(plugin);
+				return NULL;
+			}
+			else
+			{
+				/* We were able to load the plugin with lazy symbol binding.
+				 * This means we're missing some symbol.  Mark it as
+				 * unloadable and keep going so we get the info to display
+				 * to the user so they know to rebuild this plugin. */
+				plugin->unloadable = TRUE;
+			}
 		}
 
 		if (!g_module_symbol(plugin->handle, "gaim_init_plugin",
@@ -275,7 +319,6 @@
 			plugin->handle = NULL;
 
 			gaim_plugin_destroy(plugin);
-
 			return NULL;
 		}
 		gaim_init_plugin = unpunned;
@@ -285,18 +328,20 @@
 
 		if (loader == NULL) {
 			gaim_plugin_destroy(plugin);
-
 			return NULL;
 		}
 
 		gaim_init_plugin = GAIM_PLUGIN_LOADER_INFO(loader)->probe;
 	}
 
-	plugin->error = NULL;
+#ifdef _WIN32
+		/* Restore the original error mode */
+		SetErrorMode(old_error_mode);
+#endif
 
-	if (!gaim_init_plugin(plugin) || plugin->info == NULL) {
+	if (!gaim_init_plugin(plugin) || plugin->info == NULL)
+	{
 		gaim_plugin_destroy(plugin);
-
 		return NULL;
 	}
 
@@ -304,11 +349,12 @@
 			plugin->info->major_version != GAIM_MAJOR_VERSION ||
 			plugin->info->minor_version > GAIM_MINOR_VERSION)
 	{
-		gaim_debug_error("plugins", "%s is unloadable: API version mismatch %d.%d.x (need %d.%d.x)\n",
-						 plugin->path, plugin->info->major_version, plugin->info->minor_version,
+		plugin->error = g_strdup_printf("ABI version mismatch %d.%d.x (need %d.%d.x)",
+						 plugin->info->major_version, plugin->info->minor_version,
 						 GAIM_MAJOR_VERSION, GAIM_MINOR_VERSION);
-		gaim_plugin_destroy(plugin);
-		return NULL;
+		gaim_debug_error("plugins", "%s is unloadable: %s\n", plugin->path, plugin->error);
+		plugin->unloadable = TRUE;
+		return plugin;
 	}
 
 	/* If plugin is a PRPL, make sure it implements the required functions */
@@ -317,10 +363,10 @@
 		(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->login == NULL) ||
 		(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->close == NULL)))
 	{
-		gaim_debug_error("plugins", "%s is unloadable: Does not implement all required functions\n",
-						 plugin->path);
-		gaim_plugin_destroy(plugin);
-		return NULL;
+		plugin->error = g_strdup("Does not implement all required functions");
+		gaim_debug_error("plugins", "%s is unloadable: %s\n", plugin->path, plugin->error);
+		plugin->unloadable = TRUE;
+		return plugin;
 	}
 
 	return plugin;
@@ -346,11 +392,15 @@
 	GList *l;
 
 	g_return_val_if_fail(plugin != NULL, FALSE);
-	g_return_val_if_fail(plugin->error == NULL, FALSE);
 
 	if (gaim_plugin_is_loaded(plugin))
 		return TRUE;
 
+	if (gaim_plugin_is_unloadable(plugin))
+		return FALSE;
+
+	g_return_val_if_fail(plugin->error == NULL, FALSE);
+
 	/*
 	 * Go through the list of the plugin's dependencies.
 	 *
@@ -657,6 +707,14 @@
 	return plugin->loaded;
 }
 
+gboolean
+gaim_plugin_is_unloadable(const GaimPlugin *plugin)
+{
+	g_return_val_if_fail(plugin != NULL, FALSE);
+
+	return plugin->unloadable;
+}
+
 const gchar *
 gaim_plugin_get_id(const GaimPlugin *plugin) {
 	g_return_val_if_fail(plugin, NULL);

mercurial