[gaim-migrate @ 2406]

Sat, 29 Sep 2001 23:06:30 +0000

author
Eric Warmenhoven <warmenhoven@yahoo.com>
date
Sat, 29 Sep 2001 23:06:30 +0000
changeset 2393
cfef0212506b
parent 2392
c8eed9c44ab4
child 2394
8b1b56d55d75

[gaim-migrate @ 2406]
Arkadiusz Miskiewicz\'s Gadu-Gadu plugin. I was able to figure out enough polish to be able to download Gadu-Gadu, create an account, and test the plugin. Imagine my shock when I got my info and it said I was a woman. Whoops.

Also splitting plugins.c so that non-gtk stuff is in modules.c. gaim-core is almost ready for protocol implantaion.

Also fixing an IRC bug.

Also patiently waiting for anoncvs_gaim's lock in /cvsroot/gaim/gaim/pixmaps

ChangeLog file | annotate | diff | comparison | revisions
acconfig.h file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
doc/CREDITS file | annotate | diff | comparison | revisions
pixmaps/Makefile.am file | annotate | diff | comparison | revisions
pixmaps/gg_suncloud.xpm file | annotate | diff | comparison | revisions
pixmaps/gg_sunred.xpm file | annotate | diff | comparison | revisions
pixmaps/gg_sunwhitered.xpm file | annotate | diff | comparison | revisions
pixmaps/gg_sunyellow.xpm file | annotate | diff | comparison | revisions
po/POTFILES.in file | annotate | diff | comparison | revisions
src/Makefile.am file | annotate | diff | comparison | revisions
src/buddy.c file | annotate | diff | comparison | revisions
src/dialogs.c file | annotate | diff | comparison | revisions
src/gaim.h file | annotate | diff | comparison | revisions
src/module.c file | annotate | diff | comparison | revisions
src/plugins.c file | annotate | diff | comparison | revisions
src/protocols/Makefile.am file | annotate | diff | comparison | revisions
src/protocols/gg/.cvsignore file | annotate | diff | comparison | revisions
src/protocols/gg/Makefile.am file | annotate | diff | comparison | revisions
src/protocols/gg/gg.c file | annotate | diff | comparison | revisions
src/protocols/gg/libgg.c file | annotate | diff | comparison | revisions
src/protocols/gg/libgg.h file | annotate | diff | comparison | revisions
src/protocols/gg/protocol.txt file | annotate | diff | comparison | revisions
src/protocols/irc/irc.c file | annotate | diff | comparison | revisions
--- a/ChangeLog	Sat Sep 29 02:08:00 2001 +0000
+++ b/ChangeLog	Sat Sep 29 23:06:30 2001 +0000
@@ -11,6 +11,7 @@
 	* Nick Highlighting in chat
 	* Tab-completion for nicks in chat (thanks to Sean Egan)
 	* Large internal reworkings
+	* New Protocol: Gadu-Gadu, written by Arkadiusz Miskiewicz
 
 version 0.44 (09/20/2001):
 	* More sane scaling of buddy icons (intelligently scale to
--- a/acconfig.h	Sat Sep 29 02:08:00 2001 +0000
+++ b/acconfig.h	Sat Sep 29 23:06:30 2001 +0000
@@ -17,7 +17,6 @@
 #undef ARTSC_SOUND
 #undef _REENTRANT
 #undef NEED_GNOMESUPPORT_H
-#undef NEED_SOCKLEN_T
 #undef ZEPHYR_INT32
 #undef ZEPHYR_USES_KERBEROS
 #ifndef STATIC_PROTO_INIT
--- a/configure.ac	Sat Sep 29 02:08:00 2001 +0000
+++ b/configure.ac	Sat Sep 29 23:06:30 2001 +0000
@@ -32,8 +32,6 @@
 AC_TYPE_SIGNAL
 AC_FUNC_STRFTIME
 AC_CHECK_FUNCS(socket strdup strstr atexit getaddrinfo)
-AC_TRY_COMPILE([#include <sys/types.h>
-#include <sys/socket.h>], [socklen_t slen;],,[AC_DEFINE(NEED_SOCKLEN_T)])
 
 dnl Checks for getopt in standard library
 AC_CHECK_FUNCS(getopt_long , , [LIBOBJS="$LIBOBJS getopt.o getopt1.o"] ) 
@@ -48,7 +46,7 @@
 AC_ARG_ENABLE(prpls,   [  --disable-prpls         don't build dynamic protocol plugins],,enable_prpls=yes)
 AC_ARG_WITH(static-prpls,    [  --with-static-prpls     link in certain protocols statically],[STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`],STATIC_PRPLS="oscar toc")
 if test "x$STATIC_PRPLS" = "xall" ; then
-	STATIC_PRPLS="icq irc jabber msn napster oscar toc yahoo zephyr"
+	STATIC_PRPLS="gg icq irc jabber msn napster oscar toc yahoo zephyr"
 fi
 AC_SUBST(STATIC_PRPLS)
 STATIC_LINK_LIBS=
@@ -59,6 +57,7 @@
 	extern_init="$extern_init extern void ${i}_init(struct prpl *);"
 	load_proto="$load_proto load_protocol(${i}_init, sizeof(struct prpl));"
 	case $i in
+		gg) static_gg=yes ;;
 		icq) static_icq=yes ;;
 		irc) static_irc=yes ;;
 		jabber) static_jabber=yes ;;
@@ -71,6 +70,7 @@
 		*) echo "Invalid static protocol $i!!" ; exit ;;
 	esac
 done
+AM_CONDITIONAL(STATIC_GG, test "x$static_gg" = "xyes")
 AM_CONDITIONAL(STATIC_ICQ, test "x$static_icq" = "xyes")
 AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes")
 AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes")
@@ -358,6 +358,7 @@
            sounds/Makefile
 	   src/Makefile
 	   src/protocols/Makefile
+	   src/protocols/gg/Makefile
 	   src/protocols/icq/Makefile
 	   src/protocols/irc/Makefile
 	   src/protocols/jabber/Makefile
--- a/doc/CREDITS	Sat Sep 29 02:08:00 2001 +0000
+++ b/doc/CREDITS	Sat Sep 29 23:06:30 2001 +0000
@@ -36,6 +36,8 @@
 	A healthy amount of patches for the Jabber plugin
 Neil Sanchala
 	Wrote most of the Zephyr plugin
+Arkadiusz Miskiewicz
+	Wrote the Gadu-Gadu plugin
 	
 David Prater    <IM: dRaven43>          draven@tcsx.net   
 	Log and Colour Button Images
--- a/pixmaps/Makefile.am	Sat Sep 29 02:08:00 2001 +0000
+++ b/pixmaps/Makefile.am	Sat Sep 29 23:06:30 2001 +0000
@@ -34,6 +34,10 @@
 		fontface2.xpm			\
 		free_icon.xpm			\
 		gaim.xpm			\
+		gg_suncloud.xpm			\
+		gg_sunred.xpm			\
+		gg_sunwhitered.xpm		\
+		gg_sunyellow.xpm		\
 		gnome_add.xpm			\
 		gnome_preferences.xpm		\
 		gnome_remove.xpm 		\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pixmaps/gg_suncloud.xpm	Sat Sep 29 23:06:30 2001 +0000
@@ -0,0 +1,28 @@
+/* XPM */
+static char * gg_suncloud_xpm[] = {
+"18 18 7 1",
+" 	c None",
+".	c #FF0000",
+"+	c #7F7F7F",
+"@	c #FFFF00",
+"#	c #000000",
+"$	c #00007F",
+"%	c #00FFFF",
+"                  ",
+"        .         ",
+"        .+        ",
+"   .    .+   .    ",
+"    . ..@.. .+    ",
+"     .@@@@@.+     ",
+"    .@@#@#@@.+    ",
+"    .@@@@@@@.+    ",
+" ...@$@@@@@@@...  ",
+"   $$%$@@@#@$$$++ ",
+"  $%%%%$##@$%%%$  ",
+" $%%%%%%$$$%%%%$  ",
+" $%%%%%%%%%%%%%%$ ",
+" $%%%%%%%%%%%%%$  ",
+"  $%%%%%%%%%%%%$  ",
+"  $%%%$$$%%%$$$   ",
+"   $$$   $$$      ",
+"                  "};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pixmaps/gg_sunred.xpm	Sat Sep 29 23:06:30 2001 +0000
@@ -0,0 +1,25 @@
+/* XPM */
+static char * gg_sunred_xpm[] = {
+"19 18 4 1",
+" 	c None",
+".	c #FF0000",
+"+	c #7F7F7F",
+"@	c #000000",
+"                   ",
+"         .         ",
+"         .+        ",
+"    .    .+   .    ",
+"     . ..... .+    ",
+"      .......+     ",
+"     ...@.@...+    ",
+"     .........+    ",
+"  ...............  ",
+"     ..@...@..++++ ",
+"     ...@@@...+    ",
+"      .......+     ",
+"     .+..... .+    ",
+"    .+   .++  .+   ",
+"    +    .+        ",
+"         .+        ",
+"          +        ",
+"                   "};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pixmaps/gg_sunwhitered.xpm	Sat Sep 29 23:06:30 2001 +0000
@@ -0,0 +1,27 @@
+/* XPM */
+static char * gg_sunwhitered_xpm[] = {
+"19 18 6 1",
+" 	c None",
+".	c #7F0000",
+"+	c #FF0000",
+"@	c #7F7F7F",
+"#	c #FFFFFF",
+"$	c #000000",
+"                   ",
+"         .         ",
+"         +@        ",
+"    +    .@   +    ",
+"     . .+#+. .@    ",
+"      +#####+@     ",
+"     .##$#$##.@    ",
+"     +#######+@    ",
+"  .+.#########.+.  ",
+"     +#$###$#+@@@@ ",
+"     .##$$$##.@    ",
+"      +#####+@     ",
+"     .@.+#+. .@    ",
+"    +@   .@@  +@   ",
+"    @    +@        ",
+"         .@        ",
+"          @        ",
+"                   "};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pixmaps/gg_sunyellow.xpm	Sat Sep 29 23:06:30 2001 +0000
@@ -0,0 +1,26 @@
+/* XPM */
+static char * gg_sunyellow_xpm[] = {
+"18 18 5 1",
+" 	c None",
+".	c #FF0000",
+"+	c #7F7F7F",
+"@	c #FFFF00",
+"#	c #000000",
+"                  ",
+"        .         ",
+"        .+        ",
+"   .    .+   .    ",
+"    . ..@.. .+    ",
+"     .@@@@@.+     ",
+"    .@@#@#@@.+    ",
+"    .@@@@@@@.+    ",
+" ...@@@@@@@@@...  ",
+"    .@#@@@#@.++++ ",
+"    .@@###@@.+    ",
+"     .@@@@@.+     ",
+"    .+..@.. .+    ",
+"   .+   .++  .+   ",
+"   +    .+        ",
+"        .+        ",
+"         +        ",
+"                  "};
--- a/po/POTFILES.in	Sat Sep 29 02:08:00 2001 +0000
+++ b/po/POTFILES.in	Sat Sep 29 23:06:30 2001 +0000
@@ -1,3 +1,4 @@
+src/protocols/gg/gg.c
 src/protocols/icq/gaim_icq.c
 src/protocols/irc/irc.c
 src/protocols/jabber/jabber.c
--- a/src/Makefile.am	Sat Sep 29 02:08:00 2001 +0000
+++ b/src/Makefile.am	Sat Sep 29 23:06:30 2001 +0000
@@ -18,6 +18,7 @@
 			html.c \
 			idle.c \
 			list.c \
+			module.c \
 			multi.c \
 			perl.c \
 			plugins.c \
@@ -49,6 +50,7 @@
 		html.c \
 		idle.c \
 		list.c \
+		module.c \
 		multi.c \
 		perl.c \
 		plugins.c \
--- a/src/buddy.c	Sat Sep 29 02:08:00 2001 +0000
+++ b/src/buddy.c	Sat Sep 29 23:06:30 2001 +0000
@@ -2389,7 +2389,8 @@
 	gtk_widget_show(menuitem);
 	menuitem = gtk_menu_item_new_with_label(_("by Dir Info"));
 	gtk_menu_append(GTK_MENU(findmenu), menuitem);
-	gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(show_find_info), NULL);
+	gtk_signal_connect(GTK_OBJECT(menuitem), "activate", GTK_SIGNAL_FUNC(show_find_info),
+			   connections->data);
 	gtk_widget_show(menuitem);
 
 	setmenu = gtk_menu_new();
--- a/src/dialogs.c	Sat Sep 29 02:08:00 2001 +0000
+++ b/src/dialogs.c	Sat Sep 29 23:06:30 2001 +0000
@@ -150,6 +150,7 @@
 };
 
 struct findbyinfo {
+	struct gaim_connection *gc;
 	GtkWidget *window;
 	GtkWidget *firstentry;
 	GtkWidget *middleentry;
@@ -2058,10 +2059,7 @@
 	state = gtk_entry_get_text(GTK_ENTRY(b->stateentry));
 	country = gtk_entry_get_text(GTK_ENTRY(b->countryentry));
 
-	/* FIXME : dir search. not sure if even works; not important */
-	if (connections)
-		serv_dir_search(connections->data, first, middle, last, maiden, city, state, country,
-				"");
+	serv_dir_search(b->gc, first, middle, last, maiden, city, state, country, "");
 	destroy_dialog(NULL, b->window);
 }
 
@@ -2079,7 +2077,7 @@
 	destroy_dialog(NULL, b->window);
 }
 
-void show_find_info()
+void show_find_info(struct gaim_connection *gc)
 {
 	GtkWidget *cancel;
 	GtkWidget *ok;
@@ -2091,6 +2089,7 @@
 	GtkWidget *frame;
 
 	struct findbyinfo *b = g_new0(struct findbyinfo, 1);
+	b->gc = gc;
 	b->window = gtk_window_new(GTK_WINDOW_DIALOG);
 	gtk_window_set_policy(GTK_WINDOW(b->window), FALSE, TRUE, TRUE);
 	gtk_window_set_wmclass(GTK_WINDOW(b->window), "find_info", "Gaim");
--- a/src/gaim.h	Sat Sep 29 02:08:00 2001 +0000
+++ b/src/gaim.h	Sat Sep 29 23:06:30 2001 +0000
@@ -807,7 +807,9 @@
 /* Functions in plugins.c */
 #ifdef GAIM_PLUGINS
 extern void show_plugins(GtkWidget *, gpointer);
-extern void load_plugin (char *);
+extern struct gaim_plugin *load_plugin(char *);
+extern void unload_plugin(struct gaim_plugin *);
+extern struct gaim_plugin *reload_plugin(struct gaim_plugin *);
 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 *);
@@ -857,8 +859,8 @@
 extern void show_new_bp();
 extern void show_log(char *);
 extern void show_log_dialog(struct conversation *);
-extern void show_find_email(struct gaim_connection *gc);
-extern void show_find_info();
+extern void show_find_email(struct gaim_connection *);
+extern void show_find_info(struct gaim_connection *);
 extern void g_show_info_text(char *, ...);
 extern void show_set_info(struct gaim_connection *);
 extern void show_set_dir();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/module.c	Sat Sep 29 23:06:30 2001 +0000
@@ -0,0 +1,516 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ----------------
+ * The Plug-in plug
+ *
+ * Plugin support is currently being maintained by Mike Saraf
+ * msaraf@dwc.edu
+ *
+ * Well, I didn't see any work done on it for a while, so I'm going to try
+ * my hand at it. - Eric warmenhoven@yahoo.com
+ *
+ * Mike is my roomate.  I can assure you that he's lazy :-P  -- Rob rob@marko.net
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef GAIM_PLUGINS
+
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "gaim.h"
+
+/* ------------------ Global Variables ----------------------- */
+
+GList *plugins = NULL;
+GList *callbacks = NULL;
+
+/* --------------- Function Declarations --------------------- */
+
+struct gaim_plugin *  load_plugin(char *);
+              void  unload_plugin(struct gaim_plugin *p);
+struct gaim_plugin *reload_plugin(struct gaim_plugin *p);
+
+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 Function Declarations ------------- */
+
+static void plugin_remove_callbacks(GModule *);
+
+/* ------------------ Code Below ---------------------------- */
+
+struct gaim_plugin *load_plugin(char *filename)
+{
+	struct gaim_plugin *plug;
+	GList *c = plugins;
+	char *(*gaim_plugin_init)(GModule *);
+	char *(*cfunc)();
+	char *error;
+	char *retval;
+
+	if (!g_module_supported())
+		return NULL;
+	if (filename && !strlen(filename))
+		return NULL;
+
+	while (filename && c) {
+		plug = (struct gaim_plugin *)c->data;
+		if (!strcmp(filename, g_module_name(plug->handle))) {
+			/* just need to reload plugin */
+			return reload_plugin(plug);
+		} else
+			c = g_list_next(c);
+	}
+	plug = g_malloc(sizeof *plug);
+
+	debug_printf("Loading %s\n", filename);
+	plug->handle = g_module_open(filename, 0);
+	if (!plug->handle) {
+		error = (char *)g_module_error();
+		do_error_dialog(error, _("Plugin Error"));
+		g_free(plug);
+		return NULL;
+	}
+
+	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 NULL;
+	}
+
+	retval = (*gaim_plugin_init)(plug->handle);
+	debug_printf("loaded plugin returned %s\n", retval ? retval : "NULL");
+	if (retval) {
+		plugin_remove_callbacks(plug->handle);
+		do_error_dialog(retval, _("Plugin Error"));
+		g_module_close(plug->handle);
+		g_free(plug);
+		return NULL;
+	}
+
+	plugins = g_list_append(plugins, plug);
+
+	if (g_module_symbol(plug->handle, "name", (gpointer *)&cfunc)) {
+		plug->name = (*cfunc)();
+	} else {
+		plug->name = NULL;
+	}
+
+	if (g_module_symbol(plug->handle, "description", (gpointer *)&cfunc))
+		plug->description = (*cfunc)();
+	else
+		plug->description = NULL;
+
+	save_prefs();
+	return plug;
+}
+
+static void unload_gaim_plugin(struct gaim_plugin *p)
+{
+	void (*gaim_plugin_remove)();
+
+	debug_printf("Unloading %s\n", g_module_name(p->handle));
+
+	/* Attempt to call the plugin's remove function (if there) */
+	if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
+		(*gaim_plugin_remove)();
+
+	plugin_remove_callbacks(p->handle);
+
+	plugins = g_list_remove(plugins, p);
+	g_free(p);
+	save_prefs();
+}
+
+void unload_plugin(struct gaim_plugin *p)
+{
+	GModule *handle = p->handle;
+	unload_gaim_plugin(p);
+	g_module_close(handle);
+}
+
+static gboolean unload_timeout(gpointer handle)
+{
+	g_module_close(handle);
+	return FALSE;
+}
+
+void gaim_plugin_unload(GModule *handle)
+{
+	g_timeout_add(5000, unload_timeout, handle);
+}
+
+/* Do unload/load cycle of plugin. */
+struct gaim_plugin *reload_plugin(struct gaim_plugin *p)
+{
+	char file[1024];
+	GModule *handle = p->handle;
+
+	strncpy(file, g_module_name(handle), sizeof(file));
+	file[sizeof(file) - 1] = '\0';
+
+	debug_printf("Reloading %s\n", file);
+
+	/* Unload */
+	unload_plugin(p);
+
+	/* Load */
+	return load_plugin(file);
+}
+
+/* Remove all callbacks associated with plugin handle */
+static void plugin_remove_callbacks(GModule *handle)
+{
+	GList *c = callbacks;
+	struct gaim_callback *g;
+
+	debug_printf("%d callbacks to search\n", g_list_length(callbacks));
+
+	while (c) {
+		g = (struct gaim_callback *)c->data;
+		if (g->handle == handle) {
+			c = g_list_next(c);
+			callbacks = g_list_remove(callbacks, (gpointer)g);
+			debug_printf("Removing callback, %d remain\n", g_list_length(callbacks));
+		} else
+			c = g_list_next(c);
+	}
+}
+
+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;
+	call->event = which;
+	call->function = func;
+	call->data = data;
+
+	callbacks = g_list_append(callbacks, call);
+	debug_printf("Adding callback %d\n", g_list_length(callbacks));
+}
+
+void gaim_signal_disconnect(GModule *handle, enum gaim_event which, void *func)
+{
+	GList *c = callbacks;
+	struct gaim_callback *g = NULL;
+
+	while (c) {
+		g = (struct gaim_callback *)c->data;
+		if (handle == g->handle && func == g->function) {
+			callbacks = g_list_remove(callbacks, c->data);
+			g_free(g);
+			c = callbacks;
+			if (c == NULL)
+				break;
+		}
+		c = g_list_next(c);
+	}
+}
+
+#endif /* GAIM_PLUGINS */
+
+static char *event_name(enum gaim_event event)
+{
+	static char buf[128];
+	switch (event) {
+	case event_signon:
+		sprintf(buf, "event_signon");
+		break;
+	case event_signoff:
+		sprintf(buf, "event_signoff");
+		break;
+	case event_away:
+		sprintf(buf, "event_away");
+		break;
+	case event_back:
+		sprintf(buf, "event_back");
+		break;
+	case event_im_recv:
+		sprintf(buf, "event_im_recv");
+		break;
+	case event_im_send:
+		sprintf(buf, "event_im_send");
+		break;
+	case event_buddy_signon:
+		sprintf(buf, "event_buddy_signon");
+		break;
+	case event_buddy_signoff:
+		sprintf(buf, "event_buddy_signoff");
+		break;
+	case event_buddy_away:
+		sprintf(buf, "event_buddy_away");
+		break;
+	case event_buddy_back:
+		sprintf(buf, "event_buddy_back");
+		break;
+	case event_buddy_idle:
+		sprintf(buf, "event_buddy_idle");
+		break;
+	case event_buddy_unidle:
+		sprintf(buf, "event_buddy_unidle");
+		break;
+	case event_blist_update:
+		sprintf(buf, "event_blist_update");
+		break;
+	case event_chat_invited:
+		sprintf(buf, "event_chat_invited");
+		break;
+	case event_chat_join:
+		sprintf(buf, "event_chat_join");
+		break;
+	case event_chat_leave:
+		sprintf(buf, "event_chat_leave");
+		break;
+	case event_chat_buddy_join:
+		sprintf(buf, "event_chat_buddy_join");
+		break;
+	case event_chat_buddy_leave:
+		sprintf(buf, "event_chat_buddy_leave");
+		break;
+	case event_chat_recv:
+		sprintf(buf, "event_chat_recv");
+		break;
+	case event_chat_send:
+		sprintf(buf, "event_chat_send");
+		break;
+	case event_warned:
+		sprintf(buf, "event_warned");
+		break;
+	case event_error:
+		sprintf(buf, "event_error");
+		break;
+	case event_quit:
+		sprintf(buf, "event_quit");
+		break;
+	case event_new_conversation:
+		sprintf(buf, "event_new_conversation");
+		break;
+	case event_set_info:
+		sprintf(buf, "event_set_info");
+		break;
+	case event_draw_menu:
+		sprintf(buf, "event_draw_menu");
+		break;
+	case event_im_displayed_sent:
+		sprintf(buf, "event_im_displayed_sent");
+		break;
+	case event_im_displayed_rcvd:
+		sprintf(buf, "event_im_displayed_rcvd");
+		break;
+	case event_chat_send_invite:
+		sprintf(buf, "event_chat_send_invite");
+		break;
+	default:
+		sprintf(buf, "event_unknown");
+		break;
+	}
+	return buf;
+}
+
+int plugin_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4)
+{
+#ifdef USE_PERL
+	char buf[BUF_LONG];
+#endif
+#ifdef GAIM_PLUGINS
+	GList *c = callbacks;
+	struct gaim_callback *g;
+
+	while (c) {
+		void (*zero)(void *);
+		void (*one)(void *, void *);
+		void (*two)(void *, void *, void *);
+		void (*three)(void *, void *, void *, void *);
+		void (*four)(void *, void *, void *, void *, void *);
+
+		g = (struct gaim_callback *)c->data;
+		if (g->event == event && g->function !=NULL) {
+			switch (event) {
+
+				/* no args */
+			case event_blist_update:
+			case event_quit:
+				zero = g->function;
+				(*zero)(g->data);
+				break;
+
+				/* one arg */
+			case event_signon:
+			case event_signoff:
+			case event_new_conversation:
+			case event_error:
+				one = g->function;
+				(*one)(arg1, g->data);
+				break;
+
+				/* two args */
+			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_chat_leave:
+			case event_set_info:
+			case event_draw_menu:
+				two = g->function;
+				(*two)(arg1, arg2, g->data);
+				break;
+
+				/* three args */
+			case event_im_send:
+			case event_im_displayed_sent:
+			case event_chat_join:
+			case event_chat_buddy_join:
+			case event_chat_buddy_leave:
+			case event_chat_send:
+			case event_away:
+			case event_back:
+			case event_warned:
+				three = g->function;
+				(*three)(arg1, arg2, arg3, g->data);
+				break;
+
+				/* four args */
+			case event_im_recv:
+			case event_chat_recv:
+			case event_im_displayed_rcvd:
+			case event_chat_send_invite:
+			case event_chat_invited:
+				four = g->function;
+				(*four)(arg1, arg2, arg3, arg4, g->data);
+				break;
+
+			default:
+				debug_printf("unknown event %d\n", event);
+				break;
+			}
+		}
+		c = c->next;
+	}
+#endif /* GAIM_PLUGINS */
+#ifdef USE_PERL
+	switch (event) {
+	case event_signon:
+	case event_signoff:
+	case event_away:
+	case event_back:
+		g_snprintf(buf, sizeof buf, "%lu", (unsigned long)arg1);
+		break;
+	case event_im_recv:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\" %s", (unsigned long)arg1,
+			   *(char **)arg2 ? *(char **)arg2 : "(null)",
+			   *(char **)arg3 ? *(char **)arg3 : "(null)");
+		break;
+	case event_im_send:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\" %s", (unsigned long)arg1,
+			   (char *)arg2, *(char **)arg3 ? *(char **)arg3 : "(null)");
+		break;
+	case event_buddy_signon:
+	case event_buddy_signoff:
+	case event_set_info:
+	case event_buddy_away:
+	case event_buddy_back:
+	case event_buddy_idle:
+	case event_buddy_unidle:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\"", (unsigned long)arg1, (char *)arg2);
+		break;
+	case event_chat_invited:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\" \"%s\" %s", (unsigned long)arg1,
+				(char *)arg2, (char *)arg3, arg4 ? (char *)arg4 : "");
+		break;
+	case event_chat_join:
+	case event_chat_buddy_join:
+	case event_chat_buddy_leave:
+		g_snprintf(buf, sizeof buf, "%lu %d \"%s\"", (unsigned long)arg1,
+				(int)arg2, (char *)arg3);
+		break;
+	case event_chat_leave:
+		g_snprintf(buf, sizeof buf, "%lu %d", (unsigned long)arg1, (int)arg2);
+		break;
+	case event_chat_recv:
+		g_snprintf(buf, sizeof buf, "%lu %d \"%s\" %s", (unsigned long)arg1,
+				(int)arg2, (char *)arg3, (char *)arg4 ? (char *)arg4 : "(null)");
+		break;
+	case event_chat_send_invite:
+		g_snprintf(buf, sizeof buf, "%lu %d \"%s\" %s", (unsigned long)arg1,
+				(int)arg2, (char *)arg3, *(char **)arg4 ? *(char **)arg4 : "(null)");
+		break;
+	case event_chat_send:
+		g_snprintf(buf, sizeof buf, "%lu %d %s", (unsigned long)arg1, (int)arg2,
+				*(char **)arg3 ? *(char **)arg3 : "(null)");
+		break;
+	case event_warned:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\" %d", (unsigned long)arg1,
+				arg2 ? (char *)arg2 : "", (int)arg3);
+		break;
+	case event_quit:
+		buf[0] = 0;
+		break;
+	case event_new_conversation:
+		g_snprintf(buf, sizeof buf, "\"%s\"", (char *)arg1);
+		break;
+	case event_im_displayed_sent:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\" %s", (unsigned long)arg1,
+				(char *)arg2, *(char **)arg3 ? *(char **)arg3 : "(null)");
+		break;
+	case event_im_displayed_rcvd:
+		g_snprintf(buf, sizeof buf, "%lu \"%s\" %s", (unsigned long)arg1,
+				(char *)arg2, (char *)arg3 ? (char *)arg3 : "(null)");
+		break;
+	default:
+		return 0;
+	}
+	return perl_event(event_name(event), buf);
+#else
+	return 0;
+#endif
+}
+
+/* Calls the gaim_plugin_remove function in any loaded plugin that has one */
+#ifdef GAIM_PLUGINS
+void remove_all_plugins()
+{
+	GList *c = plugins;
+	struct gaim_plugin *p;
+	void (*gaim_plugin_remove)();
+
+	while (c) {
+		p = (struct gaim_plugin *)c->data;
+		if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
+			(*gaim_plugin_remove)();
+		g_free(p);
+		c = c->next;
+	}
+}
+#endif
--- a/src/plugins.c	Sat Sep 29 02:08:00 2001 +0000
+++ b/src/plugins.c	Sat Sep 29 23:06:30 2001 +0000
@@ -34,6 +34,8 @@
 #include <config.h>
 #endif
 
+#ifdef GAIM_PLUGINS
+
 #include <string.h>
 #include <sys/time.h>
 
@@ -46,10 +48,6 @@
 #include <gtk/gtk.h>
 #include "gaim.h"
 
-#ifdef GAIM_PLUGINS
-
-#include <dlfcn.h>
-
 #include "pixmaps/gnome_add.xpm"
 #include "pixmaps/gnome_remove.xpm"
 #include "pixmaps/gnome_preferences.xpm"
@@ -59,11 +57,6 @@
 #define PATHSIZE 1024	/* XXX: stolen from dialogs.c */
 
 
-/* ------------------ Global Variables ----------------------- */
-
-GList *plugins = NULL;
-GList *callbacks = NULL;
-
 /* ------------------ Local Variables ------------------------ */
 
 static GtkWidget *plugin_dialog = NULL;
@@ -84,25 +77,16 @@
 /* --------------- Function Declarations --------------------- */
 
 void show_plugins(GtkWidget *, gpointer);
-void load_plugin(char *);
-
-void gaim_signal_connect(GModule *, enum gaim_event, void *, void *);
-void gaim_signal_disconnect(GModule *, enum gaim_event, void *);
-void gaim_plugin_unload(GModule *);
 
 /* UI button callbacks */
+static void unload_plugin_cb(GtkWidget *, gpointer);
 static void plugin_reload_cb(GtkWidget *, gpointer);
 
 static const gchar *plugin_makelistname(GModule *);
-static void plugin_remove_callbacks(GModule *);
-
-static void plugin_reload(struct gaim_plugin *p);
 
 static void destroy_plugins(GtkWidget *, gpointer);
 static void load_file(GtkWidget *, gpointer);
 static void load_which_plugin(GtkWidget *, gpointer);
-static void unload_plugin(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);
@@ -166,80 +150,8 @@
 	if (plugin_dialog)
 		gtk_widget_destroy(plugin_dialog);
 	plugin_dialog = NULL;
-}
-
-void load_plugin(char *filename)
-{
-	struct gaim_plugin *plug;
-	GList *c = plugins;
-	char *(*gaim_plugin_init)(GModule *);
-	char *(*cfunc)();
-	char *error;
-	char *retval;
-
-	if (!g_module_supported())
-		return;
-	if (filename && !strlen(filename))
-		return;
-
-	while (filename && c) {
-		plug = (struct gaim_plugin *)c->data;
-		if (!strcmp(filename, g_module_name(plug->handle))) {
-			/* just need to reload plugin */
-			plugin_reload(plug);
-			return;
-		} else
-			c = g_list_next(c);
-	}
-	plug = g_malloc(sizeof *plug);
-
-	if (filename) {
-		if (last_dir)
-			g_free(last_dir);
-		last_dir = g_dirname(filename);
-	}
-
-	debug_printf("Loading %s\n", filename);
-	plug->handle = g_module_open(filename, 0);
-	if (!plug->handle) {
-		error = (char *)g_module_error();
-		do_error_dialog(error, _("Plugin Error"));
-		g_free(plug);
-		return;
-	}
-
-	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 %s\n", retval ? retval : "NULL");
-	if (retval) {
-		plugin_remove_callbacks(plug->handle);
-		do_error_dialog(retval, _("Plugin Error"));
-		g_module_close(plug->handle);
-		g_free(plug);
-		return;
-	}
-
-	plugins = g_list_append(plugins, plug);
-
-	if (g_module_symbol(plug->handle, "name", (gpointer *)&cfunc)) {
-		plug->name = (*cfunc)();
-	} else {
-		plug->name = NULL;
-	}
-
-	if (g_module_symbol(plug->handle, "description", (gpointer *)&cfunc))
-		plug->description = (*cfunc)();
-	else
-		plug->description = NULL;
 
 	update_show_plugins();
-	save_prefs();
 }
 
 void show_plugins(GtkWidget *w, gpointer data)
@@ -358,7 +270,7 @@
 	gtk_tooltips_set_tip(tooltips, reload, _("Reload the selected plugin"), "");
 
 	unload = picture_button(plugwindow, _("Unload"), gnome_remove_xpm);
-	gtk_signal_connect(GTK_OBJECT(unload), "clicked", GTK_SIGNAL_FUNC(unload_plugin), pluglist);
+	gtk_signal_connect(GTK_OBJECT(unload), "clicked", GTK_SIGNAL_FUNC(unload_plugin_cb), pluglist);
 	gtk_box_pack_start(GTK_BOX(bothbox), unload, TRUE, TRUE, 0);
 	gtk_tooltips_set_tip(tooltips, unload, _("Unload the selected plugin"), "");
 
@@ -415,11 +327,10 @@
 	}
 }
 
-static void unload_plugin(GtkWidget *w, gpointer data)
+static void unload_plugin_cb(GtkWidget *w, gpointer data)
 {
 	GList *i;
 	struct gaim_plugin *p;
-	void (*gaim_plugin_remove)();
 
 	i = GTK_LIST(pluglist)->selection;
 
@@ -428,105 +339,28 @@
 
 	p = gtk_object_get_user_data(GTK_OBJECT(i->data));
 
-	/* Attempt to call the plugin's remove function (if there) */
-	if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
-		(*gaim_plugin_remove)();
-
-	unload_immediate(p->handle);
+	unload_plugin(p);
 	update_show_plugins();
 }
 
-static void unload_for_real(void *handle)
-{
-	GList *i;
-	struct gaim_plugin *p = NULL;
-
-	i = plugins;
-	while (i) {
-		p = (struct gaim_plugin *)i->data;
-		if (handle == p->handle)
-			break;
-		p = NULL;
-		i = g_list_next(i);
-	}
-
-	if (!p)
-		return;
-
-	debug_printf("Unloading %s\n", g_module_name(p->handle));
-
-	plugin_remove_callbacks(p->handle);
-
-	plugins = g_list_remove(plugins, p);
-	g_free(p);
-	update_show_plugins();
-	save_prefs();
-}
-
-static void unload_immediate(GModule *handle)
-{
-	unload_for_real(handle);
-	g_module_close(handle);
-}
-
-static gboolean unload_timeout(gpointer handle)
-{
-	g_module_close(handle);
-	return FALSE;
-}
-
-void gaim_plugin_unload(GModule *handle)
-{
-	unload_for_real(handle);
-	g_timeout_add(5000, unload_timeout, handle);
-}
-
 static void plugin_reload_cb(GtkWidget *w, gpointer data)
 {
 	GList *i;
+	struct gaim_plugin *p;
 
 	i = GTK_LIST(pluglist)->selection;
 	if (i == NULL)
 		return;
 
 	/* Just pass off plugin to the actual function */
-	plugin_reload(gtk_object_get_user_data(GTK_OBJECT(i->data)));
-}
-
-/* Do unload/load cycle of plugin. */
-static void plugin_reload(struct gaim_plugin *p)
-{
-	char file[PATHSIZE];
-	void (*gaim_plugin_remove)();
-	GModule *handle = p->handle;
-	struct gaim_plugin *plug;
-	GList *plugs;
+	p = reload_plugin(gtk_object_get_user_data(GTK_OBJECT(i->data)));
 
-	strncpy(file, g_module_name(handle), sizeof(file));
-	file[sizeof(file) - 1] = '\0';
-
-	debug_printf("Reloading %s\n", file);
-
-	/* Unload */
-	if (g_module_symbol(handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
-		(*gaim_plugin_remove)();
-	unload_immediate(handle);
-
-	/* Load */
-	load_plugin(file);
+	update_show_plugins();
 
 	/* Try and reselect the plugin in list */
 	if (!pluglist)
 		return;
-	plugs = plugins;
-	while (plugs) {
-		plug = plugs->data;
-		if (!strcmp(file, g_module_name(plug->handle))) {
-			gtk_list_select_item(GTK_LIST(pluglist), g_list_index(plugins, plug));
-			return;
-		}
-		plugs = plugs->next;
-	}
+	gtk_list_select_item(GTK_LIST(pluglist), g_list_index(plugins, p));
 }
 
 static void list_clicked(GtkWidget *w, struct gaim_plugin *p)
@@ -594,330 +428,5 @@
 
 	return filename;
 }		
-		
-/* Remove all callbacks associated with plugin handle */
-static void plugin_remove_callbacks(GModule *handle)
-{
-	GList *c = callbacks;
-	struct gaim_callback *g;
 
-	debug_printf("%d callbacks to search\n", g_list_length(callbacks));
-
-	while (c) {
-		g = (struct gaim_callback *)c->data;
-		if (g->handle == handle) {
-			c = g_list_next(c);
-			callbacks = g_list_remove(callbacks, (gpointer)g);
-			debug_printf("Removing callback, %d remain\n", g_list_length(callbacks));
-		} else
-			c = g_list_next(c);
-	}
-}
-
-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;
-	call->event = which;
-	call->function = func;
-	call->data = data;
-
-	callbacks = g_list_append(callbacks, call);
-	debug_printf("Adding callback %d\n", g_list_length(callbacks));
-}
-
-void gaim_signal_disconnect(GModule *handle, enum gaim_event which, void *func)
-{
-	GList *c = callbacks;
-	struct gaim_callback *g = NULL;
-
-	while (c) {
-		g = (struct gaim_callback *)c->data;
-		if (handle == g->handle && func == g->function) {
-			callbacks = g_list_remove(callbacks, c->data);
-			g_free(g);
-			c = callbacks;
-			if (c == NULL)
-				break;
-		}
-		c = g_list_next(c);
-	}
-}
-
-#endif /* GAIM_PLUGINS */
-
-static char *event_name(enum gaim_event event)
-{
-	static char buf[128];
-	switch (event) {
-	case event_signon:
-		sprintf(buf, "event_signon");
-		break;
-	case event_signoff:
-		sprintf(buf, "event_signoff");
-		break;
-	case event_away:
-		sprintf(buf, "event_away");
-		break;
-	case event_back:
-		sprintf(buf, "event_back");
-		break;
-	case event_im_recv:
-		sprintf(buf, "event_im_recv");
-		break;
-	case event_im_send:
-		sprintf(buf, "event_im_send");
-		break;
-	case event_buddy_signon:
-		sprintf(buf, "event_buddy_signon");
-		break;
-	case event_buddy_signoff:
-		sprintf(buf, "event_buddy_signoff");
-		break;
-	case event_buddy_away:
-		sprintf(buf, "event_buddy_away");
-		break;
-	case event_buddy_back:
-		sprintf(buf, "event_buddy_back");
-		break;
-	case event_buddy_idle:
-		sprintf(buf, "event_buddy_idle");
-		break;
-	case event_buddy_unidle:
-		sprintf(buf, "event_buddy_unidle");
-		break;
-	case event_blist_update:
-		sprintf(buf, "event_blist_update");
-		break;
-	case event_chat_invited:
-		sprintf(buf, "event_chat_invited");
-		break;
-	case event_chat_join:
-		sprintf(buf, "event_chat_join");