changeset 1047:ece2d1543b20

[gaim-migrate @ 1057] Plugins now use GModule. Protocol plugins can be dynamically updated. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Wed, 01 Nov 2000 22:30:36 +0000
parents 4593605da0e2
children 49799527aebf
files ChangeLog FIXME configure.in plugins/ChangeLog plugins/Makefile.am plugins/autorecon.c plugins/error.c plugins/events.c plugins/filectl.c plugins/gaiminc.c plugins/gtik.c plugins/iconaway.c plugins/irc.c plugins/lagmeter.c plugins/notify.c plugins/oscar.c plugins/perl.c plugins/simple.c plugins/spellchk.c plugins/toc_commands.c src/aim.c src/buddy.c src/gaim.h src/gaimrc.c src/plugins.c src/prpl.c src/prpl.h
diffstat 27 files changed, 225 insertions(+), 319 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Nov 01 11:34:56 2000 +0000
+++ b/ChangeLog	Wed Nov 01 22:30:36 2000 +0000
@@ -9,6 +9,7 @@
 	* X-Idle support added (thanks bmiller and bryner)
 	* small change in the way away messages are displayed
 	  (Thanks Ryan C. Gordon)
+	* plugin system uses GModule now (improves portability, adds features)
 
 version 0.10.3 (10/09/2000):
 	* Segfault when viewing user info fixed
--- a/FIXME	Wed Nov 01 11:34:56 2000 +0000
+++ b/FIXME	Wed Nov 01 22:30:36 2000 +0000
@@ -1,33 +1,10 @@
 GAIM: Items to be fixed
 ------------------------
-This is just for the buddy list window now. Everything else is nearing completion.
-
-1. Enforce order. E.g. a buddy list looks like:
-	|-Buddies
-	|   |--EWarmenhoven
-	|   `--RobFlynn
-	`-Friends
-	    `--Zilding
-
-   Need to enforce that Buddies comes before Friends, and EWarmenhoven comes before RobFlynn.
-   See appropriate FIXME comments in buddy.c. DONE
-
-2. Need to make it so that when the buddy list is edited the main window properly reflects that.
-
-3. Need to modify the right-click menu for buddies, to include status for the buddy from each
-   connection that has it registered, and also to get available actions from each connection
-   that has it registered. DONE
-
-4. Need to get Aliases working again. DONE - I think
-
-5. Need to set number of buddies in group (as an option)
-
-6. Need to not hide empty groups (as an option)
-
-7. Need to do Buddy Pounces on a per-connection basis
-
-8. Need to report logins/outs to convo window
-
-9. Need to get the permit/deny stuff put back
-
-10. Need to have per-connection protocol options (e.g. which server to connect to for IRC)
+Need to make it so that when the buddy list is edited the main window properly reflects that.
+Need to set number of buddies in group (as an option)
+Need to not hide empty groups (as an option)
+Need to do Buddy Pounces on a per-connection basis
+Need to report logins/outs to convo window
+Need to get the permit/deny stuff put back
+Need to have per-connection protocol options (e.g. which server to connect to for IRC)
+Need to get the ticker working again.
--- a/configure.in	Wed Nov 01 11:34:56 2000 +0000
+++ b/configure.in	Wed Nov 01 22:30:36 2000 +0000
@@ -59,7 +59,7 @@
 fi
 
 CFLAGS="$CFLAGS -I../libfaim -I../libfaim/faim"
-LDADD="$LDADD -L../libfaim -lfaim -pthread"
+LDADD="$LDADD -L../libfaim -lfaim"
 
 if test "x$enable_gnome" = "xyes" ; then
 	if test "x$enable_panel" = "xyes" ; then
@@ -133,15 +133,8 @@
 
 CFLAGS="$CFLAGS $GTK_CFLAGS"
 
-dnl Even more X-Chat code
 if test "x$enable_plugins" = xyes ; then
-	AC_CHECK_FUNCS(dlopen, have_dl=yes)
-	if test "$have_dl" = yes; then
-		AC_CHECK_FUNCS(dlerror)
-		AC_DEFINE(GAIM_PLUGINS)
-	else
-		enable_plugins=no
-	fi
+	AC_DEFINE(GAIM_PLUGINS)
 fi
 
 dnl This was taken straight from X-Chat.
--- a/plugins/ChangeLog	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/ChangeLog	Wed Nov 01 22:30:36 2000 +0000
@@ -2,18 +2,25 @@
 	Gaim is now multi-connection based. This represents a significant
 	change. Most of the code was modified, though most of the modifications
 	were small (referencing an int as part of a struct as opposed to as a
-	global int). Such changes will require most plugins to be modified to
-	match the new function declarations and such.
+	global int). Plugins need to be modified to match the new function
+	declarations and such.
 
-	The plugin system itself was only slightly modified. However, the
-	arguments passed to signal handlers have been modified in some cases.
-	Look at the updates SIGNALS file to see what the new arguments passed
-	to your handlers are. In some cases the only change necessary will be
-	to modify the function declaration; in many cases the changes will be
-	much more substantial. The only thing really to say here is that there
-	is no more event_blist_update. This event may make a comeback, but
-	that seems doubtful at this point. The good news is that you shouldn't
-	need it anymore anyway.
+	Gaim now uses GModule from the GLib library for plugins. This brings
+	a few changes. gaim_plugin_init is now passed a GModule *, which it
+	should use for all of its callbacks. gaim_plugin_init now returns
+	char * instead of int instead of void. If gaim_plugin_init returns
+	NULL then gaim assumes everything was OK and proceeds. Otherwise, it
+	displays the error message and unloads your plugin. There is no more
+	gaim_plugin_error (at least, that gaim itself will use. You may wish
+	to simply return gaim_plugin_error() in gaim_plugin_init).
+
+	Because gaim now uses GModule, plugins are opened with RTLD_GLOBAL. I
+	had previously wanted to avoid this, but there are simply too many
+	benefits gained from using GModule to reject it for this reason. This
+	means that plugins can now call each other's functions. Beware, this
+	has good and bad implications. If you call a function, it will look
+	first in your plugin, and then in gaim's global symbol table, including
+	other plugins.
 
 	The new system allows for protocol plugins. New protocols (including
 	Yahoo, MSN, IRC, ICQ, etc) can be loaded dynamically. However, most
--- a/plugins/Makefile.am	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/Makefile.am	Wed Nov 01 22:30:36 2000 +0000
@@ -1,13 +1,14 @@
-LDFLAGS += $(LDADD) $(LIBS) -shared
+LDFLAGS += $(LDADD) $(LIBS) -ggdb -shared
+CFLAGS += -DHAVE_CONFIG_H
 SUFFIXES = .c .so
 .c.so:
-	$(CC) $(CFLAGS) -I../src -DVERSION=\"$(VERSION)\" -fPIC -o $@ $< $(LDFLAGS) $(PLUGIN_LIBS)
+	$(CC) $(CFLAGS) -I../src -DVERSION=\"$(VERSION)\" -o $@ $< $(LDFLAGS) $(PLUGIN_LIBS)
 
 if PLUGINS
 plugin_DATA = autorecon.so iconaway.so notify.so spellchk.so lagmeter.so
 plugindir = $(libdir)/gaim
 
-clean distclean clean-recursive distclean-recursive:
+clean distclean:
 	$(RM) $(plugin_DATA)
 endif
 
--- a/plugins/autorecon.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/autorecon.c	Wed Nov 01 22:30:36 2000 +0000
@@ -40,7 +40,7 @@
 		away_state = 0;
 }
 
-void gaim_plugin_init(void *handle) {
+char *gaim_plugin_init(GModule *handle) {
 	if (imaway) {
 		away_state = 1;
 		last_away = awaymessage;
@@ -50,4 +50,6 @@
 	gaim_signal_connect(handle, event_away, away_toggle, (void *)1);
 	gaim_signal_connect(handle, event_back, away_toggle, (void *)0);
 	gaim_signal_connect(handle, event_signoff, reconnect, NULL);
+
+	return NULL;
 }
--- a/plugins/error.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/error.c	Wed Nov 01 22:30:36 2000 +0000
@@ -4,7 +4,9 @@
 #include <stdlib.h>
 #include <time.h>
 
-int gaim_plugin_init(void *handle) {
+char *gaim_plugin_error(int);
+
+char *gaim_plugin_init(GModule *handle) {
 	int error;
 
 	/* so here, we load any callbacks, do the normal stuff */
@@ -13,7 +15,7 @@
 	error = rand() % 3;
 	error -= 2;
 	/* there's a 1 in 3 chance there *won't* be an error :) */
-	return error;
+	return gaim_plugin_error(error);
 }
 
 void gaim_plugin_remove() {
@@ -27,13 +29,12 @@
 	 * and do any other clean-up stuff we need to do. */
 	switch (error) {
 	case -1:
-		do_error_dialog("I'm calling the error myself", "MY BAD");
-		return NULL;
+		return "MY BAD";
 	case -2:
 		return "Internal plugin error: exiting.";
+	default:
+		return NULL;
 	}
-	/* we should never get here */
-	return NULL;
 }
 
 char *name() {
--- a/plugins/events.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/events.c	Wed Nov 01 22:30:36 2000 +0000
@@ -11,117 +11,112 @@
 #define GAIM_PLUGINS
 #include "gaim.h"
 
-void evt_signon(void *data)
+static void evt_signon(struct gaim_connection *gc, void *data)
 {
 	printf("event_signon\n");
 }
 
-void evt_signoff(void *data)
+static void evt_signoff(struct gaim_connection *gc, void *data)
 {
 	printf("event_signoff\n");
 }
 
-void evt_away(void *data)
+static void evt_away(void *data)
 {
 	printf("event_away\n");
 }
 
-void evt_back(void *data)
+static void evt_back(void *data)
 {
 	printf("event_back\n");
 }
 
-void evt_im_recv(char **who, char **what, void *data)
+static void evt_im_recv(struct gaim_connection *gc, char **who, char **what, void *data)
 {
 	printf("event_im_recv: %s %s\n", *who, *what);
 }
 
-void evt_im_send(char *who, char **what, void *data)
+static void evt_im_send(struct gaim_connection *gc, char *who, char **what, void *data)
 {
 	printf("event_im_send: %s %s\n", who, *what);
 }
 
-void evt_buddy_signon(char *who, void *data)
+static void evt_buddy_signon(struct gaim_connection *gc, char *who, void *data)
 {
 	printf("event_buddy_signon: %s\n", who);
 }
 
-void evt_buddy_signoff(char *who, void *data)
+static void evt_buddy_signoff(struct gaim_connection *gc, char *who, void *data)
 {
 	printf("event_buddy_signoff: %s\n", who);
 }
 
-void evt_buddy_away(char *who, void *data)
+static void evt_buddy_away(struct gaim_connection *gc, char *who, void *data)
 {
 	printf("event_buddy_away: %s\n", who);
 }
 
-void evt_buddy_back(char *who, void *data)
+static void evt_buddy_back(struct gaim_connection *gc, char *who, void *data)
 {
 	printf("event_buddy_back: %s\n", who);
 }
 
-void evt_blist_update(void *data)
-{
-	printf("event_blist_update\n");
-}
-
-void evt_chat_invited(char *who, char *room, char *message, void *data)
+static void evt_chat_invited(struct gaim_connection *gc, char *who, char *room, char *message, void *data)
 {
 	printf("event_chat_invited: %s %s %s\n", who, room, message);
 }
 
-void evt_chat_join(char *room, void *data)
+static void evt_chat_join(struct gaim_connection *gc, char *room, void *data)
 {
 	printf("event_chat_join: %s\n", room);
 }
 
-void evt_chat_leave(char *room, void *data)
+static void evt_chat_leave(struct gaim_connection *gc, char *room, void *data)
 {
 	printf("event_chat_leave: %s\n", room);
 }
 
-void evt_chat_buddy_join(char *room, char *who, void *data)
+static void evt_chat_buddy_join(struct gaim_connection *gc, char *room, char *who, void *data)
 {
 	printf("event_chat_buddy_join: %s %s\n", room, who);
 }
 
-void evt_chat_buddy_leave(char *room, char *who, void *data)
+static void evt_chat_buddy_leave(struct gaim_connection *gc, char *room, char *who, void *data)
 {
 	printf("event_chat_buddy_leave: %s %s\n", room, who);
 }
 
-void evt_chat_recv(char *room, char *who, char *text, void *data)
+static void evt_chat_recv(struct gaim_connection *gc, char *room, char *who, char *text, void *data)
 {
 	printf("event_chat_recv: %s %s %s\n", room, who, text);
 }
 
-void evt_chat_send(char *room, char **what, void *data)
+static void evt_chat_send(struct gaim_connection *gc, char *room, char **what, void *data)
 {
 	printf("event_chat_send: %s %s\n", room, *what);
 }
 
-void evt_warned(char *who, int level, void *data)
+static void evt_warned(struct gaim_connection *gc, char *who, int level, void *data)
 {
 	printf("event_warned: %s %d\n", who, level);
 }
 
-void evt_error(int error, void *data)
+static void evt_error(int error, void *data)
 {
 	printf("event_error: %d\n", error);
 }
 
-void evt_quit(void *data)
+static void evt_quit(void *data)
 {
 	printf("event_quit\n");
 }
 
-void evt_new_conversation(char *who, void *data)
+static void evt_new_conversation(char *who, void *data)
 {
 	printf("event_new_conversation: %s\n", who);
 }
 
-int gaim_plugin_init(void *h)
+char *gaim_plugin_init(GModule *h)
 {
 	gaim_signal_connect(h, event_signon,           evt_signon, NULL);
 	gaim_signal_connect(h, event_signoff,          evt_signoff, NULL);
@@ -133,7 +128,6 @@
 	gaim_signal_connect(h, event_buddy_signoff,    evt_buddy_signoff, NULL);
 	gaim_signal_connect(h, event_buddy_away,       evt_buddy_away, NULL);
 	gaim_signal_connect(h, event_buddy_back,       evt_buddy_back, NULL);
-	gaim_signal_connect(h, event_blist_update,     evt_blist_update, NULL);
 	gaim_signal_connect(h, event_chat_invited,     evt_chat_invited, NULL);
 	gaim_signal_connect(h, event_chat_join,        evt_chat_join, NULL);
 	gaim_signal_connect(h, event_chat_leave,       evt_chat_leave, NULL);
@@ -145,7 +139,7 @@
 	gaim_signal_connect(h, event_error,            evt_error, NULL);
 	gaim_signal_connect(h, event_quit,             evt_quit, NULL);
 	gaim_signal_connect(h, event_new_conversation, evt_new_conversation, NULL);
-	return 0;
+	return NULL;
 }
 
 char *name()
--- a/plugins/filectl.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/filectl.c	Wed Nov 01 22:30:36 2000 +0000
@@ -16,7 +16,6 @@
 static void init_file();
 static void check_file();
 
-extern void dologin(GtkWidget *, GtkWidget *);
 extern void do_quit();
 
 /* parse char * as if were word array */
@@ -45,7 +44,11 @@
 				dologin(NULL, NULL);
 			}
 		} else if (!strncasecmp(command, "signoff", 7)) {
-			signoff();
+			struct gaim_connection *gc = NULL;
+			arg1 = getarg(buffer, 1, 1);
+			if (arg1) gc = find_gaim_conn_by_name(arg1);
+			if (gc) signoff(gc);
+			else signoff_all(NULL, NULL);
 		} else if (!strncasecmp(command, "send", 4)) {
 			struct conversation *c;
 			arg1 = getarg(buffer, 1, 0);
@@ -53,7 +56,7 @@
 			c = find_conversation(arg1);
 			if (!c) c = new_conversation(arg1);
 			write_to_conv(c, arg2, WFLAG_SEND, NULL);
-			serv_send_im(arg1, arg2, 0);
+			serv_send_im(c->gc, arg1, arg2, 0);
 			free(arg1);
 			free(arg2);
 		} else if (!strncasecmp(command, "away", 4)) {
@@ -78,7 +81,7 @@
 	mtime = finfo.st_mtime;
 }
 
-void gaim_plugin_init(void *h) {
+char *gaim_plugin_init(GModule *h) {
 	handle = h;
 	init_file();
 	check = gtk_timeout_add(5000, (GtkFunction)check_file, NULL);
--- a/plugins/gaiminc.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/gaiminc.c	Wed Nov 01 22:30:36 2000 +0000
@@ -12,7 +12,7 @@
 	show_about(NULL, NULL);
 }
 
-void reverse(char **who, char **message, void *m) {
+void reverse(struct gaim_connection *gc, char **who, char **message, void *m) {
 	/* this will drive you insane. whenever you receive a message,
 	 * the text of the message (HTML and all) will be reversed. */
 	int i, l;
@@ -24,7 +24,7 @@
 
 	l = strlen(*message);
 
-	if (!strcmp(*who, current_user->username))
+	if (!strcmp(*who, gc->username))
 		return;
 
 	for (i = 0; i < l/2; i++) {
@@ -34,13 +34,13 @@
 	}
 }
 
-void bud(char *who, void *m) {
+void bud(struct gaim_connection *gc, char *who, void *m) {
 	/* whenever someone comes online, it sends them a message. if i
 	 * cared more, i'd make it so it popped up on your screen too */
-	serv_send_im(who, "Hello!", 0);
+	serv_send_im(gc, who, "Hello!", 0);
 }
 
-void gaim_plugin_init(void *handle) {
+char *gaim_plugin_init(GModule *handle) {
 	/* this is for doing something fun when we sign on */
 	gaim_signal_connect(handle, event_signon, echo_hi, NULL);
 
@@ -49,6 +49,8 @@
 
 	/* this is for doing something fun when a buddy comes online */
 	gaim_signal_connect(handle, event_buddy_signon, bud, NULL);
+
+	return NULL;
 }
 
 char *name() {
--- a/plugins/gtik.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/gtik.c	Wed Nov 01 22:30:36 2000 +0000
@@ -825,7 +825,7 @@
 
 
 	/*-----------------------------------------------------------------*/
-	int gaim_plugin_init(void *handle) { /* used to be main() */
+	char *gaim_plugin_init(GModule *handle) { /* used to be main() */
 		GtkWidget *label;
 
 		GtkWidget * vbox;
@@ -877,7 +877,7 @@
 				   (gpointer)updateOutput,"NULL");
 
 
-		return 0;
+		return NULL;
 	}
 
 
--- a/plugins/iconaway.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/iconaway.c	Wed Nov 01 22:30:36 2000 +0000
@@ -26,10 +26,12 @@
 #endif
 }
 
-void gaim_plugin_init(void *h) {
+char *gaim_plugin_init(GModule *h) {
 	handle = h;
 
 	gaim_signal_connect(handle, event_away, iconify_windows, NULL);
+
+	return NULL;
 }
 
 char *name() {
--- a/plugins/irc.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/irc.c	Wed Nov 01 22:30:36 2000 +0000
@@ -892,9 +892,9 @@
 	irc_request_buddy_update(gc);
 }
 
-struct prpl *irc_init() {
-	struct prpl *ret = g_new0(struct prpl, 1);
+static struct prpl *my_protocol = NULL;
 
+void irc_init(struct prpl *ret) {
 	ret->protocol = PROTO_IRC;
 	ret->name = irc_name;
 	ret->login = irc_login;
@@ -922,11 +922,17 @@
 	ret->chat_whisper = NULL;
 	ret->chat_send = irc_chat_send;
 	ret->keepalive = NULL;
-	
-	return ret;
+
+	my_protocol = ret;
 }
 
-int gaim_plugin_init(void *handle) {
-	load_protocl(irc_init);
-	return 0;
+char *gaim_plugin_init(GModule *handle) {
+	load_protocol(irc_init);
+	return NULL;
 }
+
+void gaim_plugin_remove() {
+	struct prpl *p = find_prpl(PROTO_IRC);
+	if (p == my_protocol)
+		unload_protocol(p);
+}
--- a/plugins/lagmeter.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/lagmeter.c	Wed Nov 01 22:30:36 2000 +0000
@@ -113,7 +113,7 @@
 	check_timeout = gtk_timeout_add(1000 * delay, (GtkFunction)send_lag, gc);
 }
 
-void gaim_plugin_init(void *h) {
+char *gaim_plugin_init(GModule *h) {
 	handle = h;
 
 	confdlg = NULL;
--- a/plugins/notify.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/notify.c	Wed Nov 01 22:30:36 2000 +0000
@@ -10,6 +10,13 @@
 	char buf[256];
 	struct conversation *cnv = find_conversation(*who);
 	GtkWindow *win;
+	char *me = g_strdup(normalize(gc->username));
+
+	if (!strcmp(me, normalize(*who))) {
+		g_free(me);
+		return;
+	}
+	g_free(me);
 
 	if (cnv == NULL)
 		cnv = new_conversation(*who);
@@ -35,7 +42,7 @@
 	}
 }
 
-void gaim_plugin_init(void *hndl) {
+char *gaim_plugin_init(GModule *hndl) {
 	handle = hndl;
 
 	gaim_signal_connect(handle, event_im_recv, received_im, NULL);
--- a/plugins/oscar.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/oscar.c	Wed Nov 01 22:30:36 2000 +0000
@@ -1372,6 +1372,8 @@
 	gtk_widget_show(button);
 }
 
+static struct prpl *my_protocol = NULL;
+
 void oscar_init(struct prpl *ret) {
 	ret->protocol = PROTO_OSCAR;
 	ret->name = oscar_name;
@@ -1405,6 +1407,8 @@
 	ret->chat_whisper = oscar_chat_whisper;
 	ret->chat_send = oscar_chat_send;
 	ret->keepalive = oscar_keepalive;
+
+	my_protocol = ret;
 }
 
 char *name() {
@@ -1415,7 +1419,13 @@
 	return "Allows gaim to use the Oscar protocol";
 }
 
-int gaim_plugin_init(void *handle) {
+char *gaim_plugin_init(GModule *handle) {
 	load_protocol(oscar_init);
-	return 0;
+	return NULL;
 }
+
+void gaim_plugin_remove() {
+	struct prpl *p = find_prpl(PROTO_OSCAR);
+	if (p == my_protocol)
+		unload_protocol(p);
+}
--- a/plugins/perl.c	Wed Nov 01 11:34:56 2000 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * This is a plugin to load perl scripts. If you don't enter
- * in a name of a script to load it will unload all perl
- * scripts. This is just to test that perl is working in gaim
- * before the UI comes in. You can use this to start building
- * perl scripts, but don't use this for anything real yet.
- *
- */
-
-#define GAIM_PLUGINS
-#include "gaim.h"
-#include "pixmaps/add.xpm"
-#include "pixmaps/cancel.xpm"
-
-char *name() {
-	return "Perl Plug";
-}
-
-char *description() {
-	return "Interface for loading perl scripts";
-}
-
-int gaim_plugin_init(void *h) {
-}
-
-static GtkWidget *config = NULL;
-static GtkWidget *entry = NULL;
-
-static void cfdes(GtkWidget *m, gpointer n) {
-	if (config) gtk_widget_destroy(config);
-	config = NULL;
-}
-
-static void do_load(GtkWidget *m, gpointer n) {
-	char *file = gtk_entry_get_text(GTK_ENTRY(entry));
-	if (!file || !strlen(file)) {
-		perl_end();
-		perl_init();
-		return;
-	}
-	perl_load_file(file);
-	gtk_widget_destroy(config);
-}
-
-void gaim_plugin_config() {
-	GtkWidget *frame;
-	GtkWidget *vbox;
-	GtkWidget *hbox;
-	GtkWidget *label;
-	GtkWidget *ok;
-	GtkWidget *cancel;
-
-	if (config) {
-		gtk_widget_show(config);
-		return;
-	}
-
-	config = gtk_window_new(GTK_WINDOW_DIALOG);
-	gtk_window_set_policy(GTK_WINDOW(config), 0, 0, 1);
-	gtk_window_set_title(GTK_WINDOW(config), "Gaim - Add Perl Script");
-	gtk_container_set_border_width(GTK_CONTAINER(config), 5);
-	gtk_signal_connect(GTK_OBJECT(config), "destroy", GTK_SIGNAL_FUNC(cfdes), 0);
-	gtk_widget_realize(config);
-	aol_icon(config->window);
-
-	frame = gtk_frame_new("Load Script");
-	gtk_container_add(GTK_CONTAINER(config), frame);
-	gtk_widget_show(frame);
-
-	vbox = gtk_vbox_new(FALSE, 5);
-	gtk_container_add(GTK_CONTAINER(frame), vbox);
-	gtk_widget_show(vbox);
-
-	hbox = gtk_hbox_new(FALSE, 5);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
-	gtk_widget_show(hbox);
-
-	label = gtk_label_new("File Name:");
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
-	gtk_widget_show(label);
-
-	entry = gtk_entry_new();
-	gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
-	gtk_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(do_load), 0);
-	gtk_widget_show(entry);
-
-	hbox = gtk_hbox_new(TRUE, 10);
-	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
-	gtk_widget_show(hbox);
-
-	ok = picture_button(config, "Load", add_xpm);
-	gtk_box_pack_start(GTK_BOX(hbox), ok, FALSE, FALSE, 5);
-	gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(do_load), 0);
-
-	cancel = picture_button(config, "Cancel", cancel_xpm);
-	gtk_box_pack_start(GTK_BOX(hbox), cancel, FALSE, FALSE, 5);
-	gtk_signal_connect(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(cfdes), 0);
-
-	gtk_widget_show(config);
-}
-
-void gaim_plugin_remove() {
-	if (config) gtk_widget_destroy(config);
-}
--- a/plugins/simple.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/simple.c	Wed Nov 01 22:30:36 2000 +0000
@@ -3,9 +3,9 @@
 #include <stdio.h>
 #include "gaim.h"
 
-static void *handle = NULL;
+static GModule *handle = NULL;
 
-void gaim_plugin_init(void *h) {
+char *gaim_plugin_init(GModule *h) {
 	printf("plugin loaded.\n");
 	handle = h;
 }
--- a/plugins/spellchk.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/spellchk.c	Wed Nov 01 22:30:36 2000 +0000
@@ -136,11 +136,12 @@
 	free(ibuf);
 }
 
-void gaim_plugin_init(void *handle) {
+char *gaim_plugin_init(GModule *handle) {
 	load_conf();
 
 	gaim_signal_connect(handle, event_im_send, substitute_words, NULL);
 	gaim_signal_connect(handle, event_chat_send, substitute_words, NULL);
+	return NULL;
 }
 
 void gaim_plugin_remove() {
--- a/plugins/toc_commands.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/plugins/toc_commands.c	Wed Nov 01 22:30:36 2000 +0000
@@ -6,7 +6,8 @@
 void enter_callback(GtkWidget *widget, GtkWidget *entry) {
 	gchar *entry_text;
 	entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
-	sflap_send(entry_text, strlen(entry_text), TYPE_DATA);
+	/* this is bad, but you really shouldn't be using this plugin */
+	sflap_send(connections->data, entry_text, strlen(entry_text), TYPE_DATA);
 }
 
 void destroy_callback(GtkWidget *widget, void *handle) {
@@ -15,7 +16,7 @@
 }
 
 GtkWidget *window;
-void gaim_plugin_init(void *h) {
+char *gaim_plugin_init(GModule *h) {
 	GtkWidget *entry;
 
 	window = gtk_window_new(GTK_WINDOW_DIALOG);
@@ -33,6 +34,8 @@
 			   h);
 
 	gtk_widget_show(window);
+
+	return NULL;
 }
 
 void gaim_plugin_remove() {
--- a/src/aim.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/aim.c	Wed Nov 01 22:30:36 2000 +0000
@@ -85,7 +85,6 @@
 	GList *c;
 	struct gaim_plugin *p;
 	void (*gaim_plugin_remove)();
-	char *error;
 
 	/* first we tell those who have requested it we're quitting */
 	plugin_event(event_quit, 0, 0, 0, 0);
@@ -94,12 +93,10 @@
 	c = plugins;
 	while (c) {
 		p = (struct gaim_plugin *)c->data;
-		gaim_plugin_remove = dlsym(p->handle, "gaim_plugin_remove");
-		if ((error = (char *)dlerror()) == NULL)
+		if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
 			(*gaim_plugin_remove)();
 		/* we don't need to worry about removing callbacks since
 		 * there won't be any more chance to call them back :) */
-		g_free(p->filename); /* why do i bother? */
 		g_free(p);
 		c = c->next;
 	}
@@ -437,12 +434,12 @@
 int main(int argc, char *argv[])
 {
 	char opt;
-	int i;
 	int opt_acct = 0, opt_help = 0, opt_version = 0,
 	    opt_user = 0, opt_login = 0, do_login_ret = -1;
 	char *opt_user_arg = NULL, *opt_login_arg = NULL;
 
 #ifdef USE_GNOME
+	int i;
 	poptContext popt_context;
 	struct poptOption popt_options[] =
 	{
--- a/src/buddy.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/buddy.c	Wed Nov 01 22:30:36 2000 +0000
@@ -788,12 +788,10 @@
 	c = plugins;
 	while (c) {
 		p = (struct gaim_plugin *)c->data;
-		gaim_plugin_remove = dlsym(p->handle, "gaim_plugin_remove");
-		if ((error = (char *)dlerror()) == NULL)
+		if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
 			(*gaim_plugin_remove)();
 		/* we don't need to worry about removing callbacks since
 		 * there won't be any more chance to call them back :) */
-		g_free(p->filename); /* why do i bother? */
 		g_free(p);
 		c = c->next;
 	}
--- a/src/gaim.h	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/gaim.h	Wed Nov 01 22:30:36 2000 +0000
@@ -196,15 +196,16 @@
 };
 
 #ifdef GAIM_PLUGINS
+#include <gmodule.h>
+
 struct gaim_plugin {
+	GModule *handle;
 	char *name;
-	char *filename;
 	char *description;
-	void *handle;
 };
 
 struct gaim_callback {
-	void *handle;
+	GModule *handle;
 	enum gaim_event event;
 	void *function;
 	void *data;
@@ -780,9 +781,9 @@
 #ifdef GAIM_PLUGINS
 extern void show_plugins(GtkWidget *, gpointer);
 extern void load_plugin (char *);
-extern void gaim_signal_connect(void *, enum gaim_event, void *, void *);
-extern void gaim_signal_disconnect(void *, enum gaim_event, void *);
-extern void gaim_plugin_unload(void *);
+extern void gaim_signal_connect(GModule *, enum gaim_event, void *, void *);
+extern void gaim_signal_disconnect(GModule *, enum gaim_event, void *);
+extern void gaim_plugin_unload(GModule *);
 #endif
 extern char *event_name(enum gaim_event);
 extern void plugin_event(enum gaim_event, void *, void *, void *, void *);
--- a/src/gaimrc.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/gaimrc.c	Wed Nov 01 22:30:36 2000 +0000
@@ -388,7 +388,7 @@
 
 		p = (struct gaim_plugin *)pl->data;
 
-		path = escape_text2(p->filename);
+		path = escape_text2(g_module_name(p->handle));
 
 		fprintf(f, "\tplugin { %s }\n", path);
 
--- a/src/plugins.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/plugins.c	Wed Nov 01 22:30:36 2000 +0000
@@ -70,14 +70,15 @@
        void show_plugins (GtkWidget *, gpointer);
        void load_plugin  (char *);
 
-       void gaim_signal_connect   (void *, enum gaim_event, void *, void *);
-       void gaim_signal_disconnect(void *, enum gaim_event, void *);
-       void gaim_plugin_unload    (void *);
+       void gaim_signal_connect   (GModule *, enum gaim_event, void *, void *);
+       void gaim_signal_disconnect(GModule *, enum gaim_event, void *);
+       void gaim_plugin_unload    (GModule *);
 
 static void destroy_plugins  (GtkWidget *, gpointer);
 static void load_file        (GtkWidget *, gpointer);
 static void load_which_plugin(GtkWidget *, gpointer);
 static void unload           (GtkWidget *, gpointer);
+static void unload_immediate (GModule *);
 static void list_clicked     (GtkWidget *, struct gaim_plugin *);
 static void update_show_plugins();
 static void hide_plugins     (GtkWidget *, gpointer);
@@ -146,59 +147,58 @@
 void load_plugin(char *filename) {
 	struct gaim_plugin *plug;
 	GList *c = plugins;
-	int (*gaim_plugin_init)();
-	char *(*gaim_plugin_error)(int);
+	char *(*gaim_plugin_init)(GModule *);
 	char *(*cfunc)();
 	char *error;
-	int retval;
-	char *plugin_error;
+	char *retval;
+	char *tmp_filename;
 
+	if (!g_module_supported()) return;
 	if (filename == NULL) return;
-	/* i shouldn't be checking based solely on path, but i'm lazy */
+
 	while (c) {
 		plug = (struct gaim_plugin *)c->data;
-		if (!strcmp(filename, plug->filename)) {
-			debug_printf( _("Already loaded %s, not reloading.\n"), filename);
-			return;
-		}
-		c = g_list_next(c);
+		if (!strcmp(filename, g_module_name(plug->handle))) {
+			void (*gaim_plugin_remove)();
+			if (g_module_symbol(plug->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
+				(*gaim_plugin_remove)();
+
+			unload_immediate(plug->handle);
+			c = plugins;
+		} else
+			c = g_list_next(c);
 	}
 	plug = g_malloc(sizeof *plug);
 	if (!g_path_is_absolute(filename))
-		plug->filename = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S,
+		tmp_filename = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S,
 			PLUGIN_DIR, filename, NULL);
 	else
-		plug->filename = g_strdup(filename);
+		tmp_filename = g_strdup(filename);
 
 	if (last_dir)
 		g_free(last_dir);
-	last_dir = g_dirname(plug->filename);
+	last_dir = g_dirname(tmp_filename);
 
-	debug_printf("Loading %s\n", filename);
-	/* do NOT `OR' with RTLD_GLOBAL, otherwise plugins may conflict
-	 * (it's really just a way to work around other people's bad
-	 * programming, by not using RTLD_GLOBAL :P ) */
-	plug->handle = dlopen(plug->filename, RTLD_LAZY);
+	debug_printf("Loading %s\n", tmp_filename);
+	plug->handle = g_module_open(tmp_filename, 0);
+	g_free(tmp_filename);
 	if (!plug->handle) {
-		error = (char *)dlerror();
+		error = (char *)g_module_error();
 		do_error_dialog(error, _("Plugin Error"));
-		g_free(plug->filename);
 		g_free(plug);
 		return;
 	}
 
-	gaim_plugin_init = dlsym(plug->handle, "gaim_plugin_init");
-	if ((error = (char *)dlerror()) != NULL) {
-		do_error_dialog(error, _("Plugin Error"));
-		dlclose(plug->handle);
-		g_free(plug->filename);
+	if (!g_module_symbol(plug->handle, "gaim_plugin_init", (gpointer *)&gaim_plugin_init)) {
+		do_error_dialog(g_module_error(), _("Plugin Error"));
+		g_module_close(plug->handle);
 		g_free(plug);
 		return;
 	}
 
 	retval = (*gaim_plugin_init)(plug->handle);
 	debug_printf("loaded plugin returned %d\n", retval);
-	if (retval < 0) {
+	if (retval) {
 		GList *c = callbacks;
 		struct gaim_callback *g;
 		while (c) {
@@ -216,28 +216,20 @@
 				c = g_list_next(c);
 			}
 		}
-		gaim_plugin_error = dlsym(plug->handle, "gaim_plugin_error");
-		if ((error = (char *)dlerror()) == NULL) {
-			plugin_error = (*gaim_plugin_error)(retval);
-			if (plugin_error)
-				do_error_dialog(plugin_error, _("Plugin Error"));
-		}
-		dlclose(plug->handle);
-		g_free(plug->filename);
+		do_error_dialog(retval, _("Plugin Error"));
+		g_module_close(plug->handle);
 		g_free(plug);
 		return;
 	}
 
 	plugins = g_list_append(plugins, plug);
 
-	cfunc = dlsym(plug->handle, "name");
-	if ((error = (char *)dlerror()) == NULL)
+	if (g_module_symbol(plug->handle, "name", (gpointer *)&cfunc))
 		plug->name = (*cfunc)();
 	else
 		plug->name = NULL;
 
-	cfunc = dlsym(plug->handle, "description");
-	if ((error = (char *)dlerror()) == NULL)
+	if (g_module_symbol(plug->handle, "description", (gpointer *)&cfunc))
 		plug->description = (*cfunc)();
 	else
 		plug->description = NULL;
@@ -332,7 +324,7 @@
 
 	while (plugs) {
 		p = (struct gaim_plugin *)plugs->data;
-		label = gtk_label_new(p->filename);
+		label = gtk_label_new(g_module_name(p->handle));
 		list_item = gtk_list_item_new();
 		gtk_container_add(GTK_CONTAINER(list_item), label);
 		gtk_signal_connect(GTK_OBJECT(list_item), "select",
@@ -377,7 +369,7 @@
 	gtk_list_clear_items(GTK_LIST(pluglist), 0, -1);
 	while (plugs) {
 		p = (struct gaim_plugin *)plugs->data;
-		label = gtk_label_new(p->filename);
+		label = gtk_label_new(g_module_name(p->handle));
 		list_item = gtk_list_item_new();
 		gtk_container_add(GTK_CONTAINER(list_item), label);
 		gtk_signal_connect(GTK_OBJECT(list_item), "select",
@@ -402,7 +394,6 @@
 	GList *i;
 	struct gaim_plugin *p;
 	void (*gaim_plugin_remove)();
-	char *error;
 
 	i = GTK_LIST(pluglist)->selection;
 
@@ -411,16 +402,13 @@
 	p = gtk_object_get_user_data(GTK_OBJECT(i->data));
 
 	/* Attempt to call the plugin's remove function (if there) */
-	gaim_plugin_remove = dlsym(p->handle, "gaim_plugin_remove");
-	if ((error = (char *)dlerror()) == NULL)
+	if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
 		(*gaim_plugin_remove)();
 
-	gaim_plugin_unload(p->handle);
+	unload_immediate(p->handle);
 }
 
-/* gaim_plugin_unload serves 2 purposes: 1. so plugins can unload themselves
- * 					 2. to make my life easier */
-void gaim_plugin_unload(void *handle) {
+static void unload_for_real(void *handle) {
 	GList *i;
 	struct gaim_plugin *p = NULL;
 	GList *c = callbacks;
@@ -438,7 +426,7 @@
 	if (!p)
 		return;
 
-	sprintf(debug_buff, "Unloading %s\n", p->filename);
+	sprintf(debug_buff, "Unloading %s\n", g_module_name(p->handle));
 	debug_print(debug_buff);
 
 	sprintf(debug_buff, "%d callbacks to search\n", g_list_length(callbacks));
@@ -462,14 +450,27 @@
 	}
 
 	plugins = g_list_remove(plugins, p);
-	g_free(p->filename);
-	/* we don't dlclose(p->handle) in case if we still need code from the plugin later */
 	g_free(p);
 	if (config) gtk_widget_set_sensitive(config, 0);
 	update_show_plugins();
 	save_prefs();
 }
 
+void unload_immediate(GModule *handle) {
+	unload_for_real(handle);
+	g_module_close(handle);
+}
+
+static gint unload_timeout(GModule *handle) {
+	g_module_close(handle);
+	return FALSE;
+}
+
+void gaim_plugin_unload(GModule *handle) {
+	unload_for_real(handle);
+	gtk_timeout_add(5000, (GtkFunction)unload_timeout, handle);
+}
+
 void list_clicked(GtkWidget *w, struct gaim_plugin *p) {
 	gchar *temp;
 	guint text_len;
@@ -487,8 +488,7 @@
 	g_free(temp);
 
 	/* Find out if this plug-in has a configuration function */
-	gaim_plugin_config = dlsym(p->handle, "gaim_plugin_config");
-	if ((error = (char *)dlerror()) == NULL) {
+	if (g_module_symbol(p->handle, "gaim_plugin_config", (gpointer *)&gaim_plugin_config)) {
 		confighandle = gtk_signal_connect(GTK_OBJECT(config), "clicked",
 				   GTK_SIGNAL_FUNC(gaim_plugin_config), NULL);
 		gtk_widget_set_sensitive(config, 1);
@@ -506,7 +506,7 @@
 	confighandle = 0;
 }
 
-void gaim_signal_connect(void *handle, enum gaim_event which,
+void gaim_signal_connect(GModule *handle, enum gaim_event which,
 			 void *func, void *data) {
 	struct gaim_callback *call = g_new0(struct gaim_callback, 1);
 	call->handle = handle;
@@ -519,7 +519,7 @@
 	debug_print(debug_buff);
 }
 
-void gaim_signal_disconnect(void *handle, enum gaim_event which, void *func) {
+void gaim_signal_disconnect(GModule *handle, enum gaim_event which, void *func) {
 	GList *c = callbacks;
 	struct gaim_callback *g = NULL;
 
@@ -672,6 +672,12 @@
 			/* struct gaim_connection *, char * */
 			case event_chat_join:
 			case event_chat_leave:
+			case event_buddy_signon:
+			case event_buddy_signoff:
+			case event_buddy_away:
+			case event_buddy_back:
+			case event_buddy_idle:
+			case event_buddy_unidle:
 				{
 					void (*function)(struct gaim_connection *, char *, void *) =
 										g->function;
@@ -680,12 +686,6 @@
 				break;
 
 			/* char * */
-			case event_buddy_signon:
-			case event_buddy_signoff:
-			case event_buddy_away:
-			case event_buddy_back:
-			case event_buddy_idle:
-			case event_buddy_unidle:
 			case event_new_conversation:
 				{
 					void (*function)(char *, void *) = g->function;
--- a/src/prpl.c	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/prpl.c	Wed Nov 01 22:30:36 2000 +0000
@@ -52,26 +52,29 @@
 	struct prpl *old;
 	GSList *n = protocols;
 	pi(p);
-	if (old = find_prpl(p->protocol)) {
-		GSList *c = connections;
-		struct gaim_connection *g;
-		while (c) {
-			g = (struct gaim_connection *)c->data;
-			if (g->prpl == old) {
-				char buf[256];
-				g_snprintf(buf, sizeof buf, _("%s was using %s, which got replaced."
-								" %s is now offline."), g->username,
-								(*p->name)(), g->username);
-				do_error_dialog(buf, _("Disconnect"));
-				signoff(g);
-				c = connections;
-			} else
-				c = c->next;
-		}
-		protocols = g_slist_remove(protocols, old);
-		g_free(old);
+	if (old = find_prpl(p->protocol))
+		unload_protocol(old);
+	protocols = g_slist_insert_sorted(protocols, p, (GCompareFunc)proto_compare);
+}
+
+void unload_protocol(struct prpl *p) {
+	GSList *c = connections;
+	struct gaim_connection *g;
+	while (c) {
+		g = (struct gaim_connection *)c->data;
+		if (g->prpl == p) {
+			char buf[256];
+			g_snprintf(buf, sizeof buf, _("%s was using %s, which got removed."
+							" %s is now offline."), g->username,
+							(*p->name)(), g->username);
+			do_error_dialog(buf, _("Disconnect"));
+			signoff(g);
+			c = connections;
+		} else
+			c = c->next;
 	}
-	protocols = g_slist_insert_sorted(protocols, p, (GCompareFunc)proto_compare);
+	protocols = g_slist_remove(protocols, p);
+	g_free(p);
 }
 
 void static_proto_init()
--- a/src/prpl.h	Wed Nov 01 11:34:56 2000 +0000
+++ b/src/prpl.h	Wed Nov 01 22:30:36 2000 +0000
@@ -96,6 +96,7 @@
 
 /* this is what should actually load the protocol. pass it the protocol's initializer */
 void load_protocol(proto_init);
+void unload_protocol(struct prpl *);
 
 struct prpl *find_prpl(int);