changeset 5205:fefad67de2c7

[gaim-migrate @ 5573] I had a damn good commit message, but it was eaten. Let's try it again. Announcing, Gaim Plugin API version 2.0, or GPAPIV2.0 for short. There are lots'a cool thingies here. Okay now, this isn't as cool as the previous message, but: 1) There's now a single entry function for all plugin types. It returns a detailed information structure on the plugin. This removes a lot of the ugliness from old plugins. Oh yeah, libicq wasn't converted to this, so if you use it, well, you shouldn't have used it anyway, but now you can't! bwahahaha. Use AIM/ICQ. 2) There are now 3 types of plugins: Standard, Loader, and Protocol plugins. Standard plugins are, well, standard, compiled plugins. Loader plugins load other plugins. For example, the perl support is now a loader plugin. It loads perl scripts. In the future, we'll have Ruby and Python loader plugins. Protocol plugins are, well, protocol plugins... yeah... 3) Plugins have unique IDs, so they can be referred to or automatically updated from a plugin database in the future. Neat, huh? 4) Plugins will have dependency support in the future, and can be hidden, so if you have, say, a logging core plugin, it won't have to show up, but then you load the GTK+ logging plugin and it'll auto-load the core plugin. Core/UI split plugins! 5) There will eventually be custom plugin signals and RPC of some sort, for the core/ui split plugins. So, okay, back up .gaimrc. I'd like to thank my parents for their support, javabsp for helping convert a bunch of protocol plugins, and Etan for helping convert a bunch of standard plugins. Have fun. If you have any problems, please let me know, but you probably won't have anything major happen. You will have to convert your plugins, though, and I'm not guaranteeing that all perl scripts will still work. I'll end up changing the perl script API eventually, so I know they won't down the road. Don't worry, though. It'll be mass cool. faceprint wants me to just commit the damn code already. So, here we go!!! .. .. I need a massage. From a young, cute girl. Are there any young, cute girls in the audience? IM me plz k thx. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Fri, 25 Apr 2003 06:47:33 +0000
parents 44de70702205
children ffac5e345451
files ChangeLog configure.ac configure.in plugins/.cvsignore plugins/Makefile.am plugins/autorecon.c plugins/chatlist.c plugins/docklet/docklet.c plugins/error.c plugins/events.c plugins/filectl.c plugins/gaiminc.c plugins/gestures/gestures.c plugins/gtik.c plugins/history.c plugins/iconaway.c plugins/idle.c plugins/mailchk.c plugins/notify.c plugins/perl/.cvsignore plugins/perl/Makefile.am plugins/perl/perl.c plugins/raw.c plugins/simple.c plugins/spellchk.c plugins/ticker/ticker.c plugins/timestamp.c src/Makefile.am src/away.c src/buddy.c src/buddy_chat.c src/conversation.c src/core.c src/core.h src/dialogs.c src/event.c src/event.h src/gaimrc.c src/gtkconv.c src/gtkplugin.c src/gtkplugin.h src/gtkpounce.c src/idle.c src/list.c src/log.c src/main.c src/module.c src/multi.c src/multi.h src/perl.c src/plugin.c src/plugin.h src/prefs.c src/protocols/Makefile.am src/protocols/gg/gg.c src/protocols/icq/gaim_icq.c src/protocols/irc/irc.c src/protocols/jabber/jabber.c src/protocols/msn/msn.c src/protocols/napster/napster.c src/protocols/oscar/oscar.c src/protocols/toc/toc.c src/protocols/yahoo/yahoo.c src/protocols/zephyr/zephyr.c src/prpl.c src/prpl.h src/server.c src/themes.c src/util.c
diffstat 69 files changed, 5623 insertions(+), 3913 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Apr 24 03:52:25 2003 +0000
+++ b/ChangeLog	Fri Apr 25 06:47:33 2003 +0000
@@ -1,5 +1,10 @@
 Gaim: The Pimpin' Penguin IM Clone that's good for the soul!
 
+version 0.63:
+	* A rewrite of the plugin API. Plugin authors will need to change their
+	  code based off the changes found in other plugins.
+	* Perl script support is now provided in the perl plugin.
+
 version 0.62 (04/23/2003):
 	* Keyboard shortcuts in the buddy list work again (Thanks Joe 
 	  Clarke).
--- a/configure.ac	Thu Apr 24 03:52:25 2003 +0000
+++ b/configure.ac	Fri Apr 25 06:47:33 2003 +0000
@@ -243,7 +243,7 @@
 if test "$enable_perl" = yes ; then
 	AC_SUBST(PERL_CFLAGS)
 	AC_SUBST(PERL_LIBS)
-	AC_DEFINE(USE_PERL, 1, [Define if Perl is enabled.])
+	AM_CONDITIONAL(USE_PERL, test "x$enable_perl" = "xyes")
 
 	dnl This is almost definitely wrong, but in case there's
 	dnl something I'm missing, I'll leave it in.
@@ -399,6 +399,7 @@
 	   plugins/Makefile
 	   plugins/docklet/Makefile
 	   plugins/gestures/Makefile
+	   plugins/perl/Makefile
 	   plugins/ticker/Makefile
 	   po/Makefile.in 
            sounds/Makefile
--- a/configure.in	Thu Apr 24 03:52:25 2003 +0000
+++ b/configure.in	Fri Apr 25 06:47:33 2003 +0000
@@ -374,6 +374,7 @@
 	   plugins/Makefile
 	   plugins/docklet/Makefile
 	   plugins/gestures/Makefile
+	   plugins/perl/Makefile
 	   plugins/ticker/Makefile
 	   po/Makefile.in 
            sounds/Makefile
--- a/plugins/.cvsignore	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/.cvsignore	Fri Apr 25 06:47:33 2003 +0000
@@ -2,8 +2,6 @@
 Makefile.in
 .deps
 .libs
-autorecon.dll
-iconaway.dll
-spellchk.dll
+*.dll
 *.la
 *.lo
--- a/plugins/Makefile.am	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/Makefile.am	Fri Apr 25 06:47:33 2003 +0000
@@ -1,10 +1,13 @@
-SUBDIRS = docklet gestures ticker
+DIST_SUBDIRS = docklet gestures perl ticker
+
+if USE_PERL
+PERL_DIR = perl
+endif
+
+SUBDIRS = docklet gestures $(PERL_DIR) ticker
 
 plugindir = $(libdir)/gaim
 
-#CFLAGS += -I\$(top_srcdir) -I\$(top_srcdir)/src -DVERSION=\"$(VERSION)\" $(DEBUG_CFLAGS)
-
-
 autorecon_la_LDFLAGS = -module -avoid-version
 iconaway_la_LDFLAGS  = -module -avoid-version
 notify_la_LDFLAGS    = -module -avoid-version
@@ -32,7 +35,7 @@
 timestamp_la_SOURCES = timestamp.c
 idle_la_SOURCES      = idle.c
 
-endif
+endif # PLUGINS
 
 EXTRA_DIST = \
 	ChangeLog PERL-HOWTO HOWTO SIGNALS \
@@ -51,7 +54,7 @@
 #
 SUFFIXES = .c .so
 .c.so:
-	$(LIBTOOL) --mode=compile $(CC) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS)
+	$(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS)
 	$(LIBTOOL) --mode=link    $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(PLUGIN_LIBS)
 	@rm -f tmp$@.lo tmp$@.o libtmp$@.la
 	@cp .libs/libtmp$@.so* $@
--- a/plugins/autorecon.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/autorecon.c	Fri Apr 25 06:47:33 2003 +0000
@@ -1,8 +1,10 @@
 #include "config.h"
 
+#if 0
 #ifndef GAIM_PLUGINS
 #define GAIM_PLUGINS
 #endif
+#endif
 
 #include "gaim.h"
 #include "prpl.h"
@@ -11,6 +13,8 @@
 #include "win32dep.h"
 #endif
 
+#define AUTORECON_PLUGIN_ID "core-autorecon"
+
 G_MODULE_IMPORT GSList *gaim_accounts;
 
 #define INITIAL 8000
@@ -48,41 +52,62 @@
 	}
 }
 
-/*
- *  EXPORTED FUNCTIONS
- */
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	hash = g_hash_table_new(g_int_hash, g_int_equal);
 
-struct gaim_plugin_description desc; 
-G_MODULE_EXPORT struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Autoreconnect"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("When you are kicked offline, this reconnects you."));
-	desc.authors = g_strdup("Eric Warmenhoven &lt;eric@warmenhoven.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
+	gaim_signal_connect(plugin, event_signoff, reconnect, NULL);
+
+	return TRUE;
 }
 
-G_MODULE_EXPORT char *name() {
-	return _("Auto Reconnect");
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+	if (tim)
+		g_source_remove(tim);
+
+	gaim_signal_disconnect(plugin, event_signoff, reconnect);
+
+	g_hash_table_destroy(hash);
+
+	hash = NULL;
+	tim = 0;
+
+	return TRUE;
 }
 
-G_MODULE_EXPORT char *description() {
-	return _("When you are kicked offline, this reconnects you.");
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	AUTORECON_PLUGIN_ID,                              /**< id             */
+	N_("Auto-Reconnect"),                             /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("When you are kicked offline, this reconnects you."), 
+	                                                  /**  description    */
+	N_("When you are kicked offline, this reconnects you."), 
+	"Eric Warmenhoven <eric@warmenhoven.org>",        /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
 
-G_MODULE_EXPORT char *gaim_plugin_init(GModule *handle) {
-	hash = g_hash_table_new(g_int_hash, g_int_equal);
-
-	gaim_signal_connect(handle, event_signoff, reconnect, NULL);
-
-	return NULL;
-}
-
-G_MODULE_EXPORT void gaim_plugin_remove() {
-	if (tim)
-		g_source_remove(tim);
-	g_hash_table_destroy(hash);
-	hash = NULL;
-	tim = 0;
-}
+GAIM_INIT_PLUGIN(autorecon, __init_plugin, info);
--- a/plugins/chatlist.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/chatlist.c	Fri Apr 25 06:47:33 2003 +0000
@@ -396,7 +396,7 @@
 
 struct gaim_plugin_description desc; 
 struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
+	desc.api_version = GAIM_PLUGIN_API_VERSION;
 	desc.name = g_strdup(_("Chat List"));
 	desc.version = g_strdup(VERSION);
 	desc.description = g_strdup(_("Allows you to add chat rooms to your buddy list."));
--- a/plugins/docklet/docklet.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/docklet/docklet.c	Fri Apr 25 06:47:33 2003 +0000
@@ -28,14 +28,13 @@
 
 /* includes */
 #include <gtk/gtk.h>
+#include "gtkplugin.h"
 #include "gaim.h"
 #include "sound.h"
 #include "eggtrayicon.h"
 #include "gtklist.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
+#define DOCKLET_PLUGIN_ID "gtk-docklet"
 
 /* types */
 enum docklet_status {
@@ -51,7 +50,7 @@
 /* functions */
 static gboolean docklet_create();
 static gboolean docklet_update_status();
-void gaim_plugin_remove();
+static gboolean plugin_unload(GaimPlugin *plugin);
 
 /* globals */
 static EggTrayIcon *docklet = NULL;
@@ -326,7 +325,7 @@
 		   something messed up. try destroying it before we proceed,
 		   although docklet_refcount may be all hosed. hopefully won't happen. */
 		debug_printf("Tray Icon: trying to create icon but it already exists?\n");
-		gaim_plugin_remove();
+		plugin_unload(NULL);
 	}
 
 	docklet = egg_tray_icon_new("Gaim");
@@ -394,24 +393,28 @@
 static void gaim_new_conversation(char *who, void *data) {
 } */
 
-char *gaim_plugin_init(GModule *handle) {
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
 	docklet_create(NULL);
 
-	gaim_signal_connect(handle, event_signon, gaim_signon, NULL);
-	gaim_signal_connect(handle, event_signoff, gaim_signoff, NULL);
-	gaim_signal_connect(handle, event_connecting, gaim_connecting, NULL);
-	gaim_signal_connect(handle, event_away, gaim_away, NULL);
-	gaim_signal_connect(handle, event_im_recv, gaim_im_recv, NULL);
-/*	gaim_signal_connect(handle, event_buddy_signon, gaim_buddy_signon, NULL);
-	gaim_signal_connect(handle, event_buddy_signoff, gaim_buddy_signoff, NULL);
-	gaim_signal_connect(handle, event_buddy_away, gaim_buddy_away, NULL);
-	gaim_signal_connect(handle, event_buddy_back, gaim_buddy_back, NULL);
-	gaim_signal_connect(handle, event_new_conversation, gaim_new_conversation, NULL); */
+	gaim_signal_connect(plugin, event_signon, gaim_signon, NULL);
+	gaim_signal_connect(plugin, event_signoff, gaim_signoff, NULL);
+	gaim_signal_connect(plugin, event_connecting, gaim_connecting, NULL);
+	gaim_signal_connect(plugin, event_away, gaim_away, NULL);
+	gaim_signal_connect(plugin, event_im_recv, gaim_im_recv, NULL);
+/*	gaim_signal_connect(plugin, event_buddy_signon, gaim_buddy_signon, NULL);
+	gaim_signal_connect(plugin, event_buddy_signoff, gaim_buddy_signoff, NULL);
+	gaim_signal_connect(plugin, event_buddy_away, gaim_buddy_away, NULL);
+	gaim_signal_connect(plugin, event_buddy_back, gaim_buddy_back, NULL);
+	gaim_signal_connect(plugin, event_new_conversation, gaim_new_conversation, NULL); */
 
-	return NULL;
+	return TRUE;
 }
 
-void gaim_plugin_remove() {
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
 	if (GTK_WIDGET_VISIBLE(docklet)) {
 		gaim_gtk_blist_docklet_remove();
 	}
@@ -430,9 +433,13 @@
 	gaim_sound_set_mute(FALSE);
 
 	debug_printf("Tray Icon: removed\n");
+
+	return TRUE;
 }
 
-GtkWidget *gaim_plugin_config_gtk() {
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
+{
 	GtkWidget *frame;
 	GtkWidget *vbox, *hbox;
 	GtkWidget *toggle;
@@ -458,21 +465,45 @@
 	return frame;
 }
 
-struct gaim_plugin_description desc; 
-struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("System Tray Icon"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Interacts with a Notification Area applet (in GNOME or KDE, for example) to display the current status of Gaim, allow fast access to commonly used functions, and to toggle display of the buddy list or login window. Also allows messages to be queued until the icon is clicked, similar to ICQ."));
-	desc.authors = g_strdup("Robert McQueen &lt;robot101@debian.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	DOCKLET_PLUGIN_ID,                                /**< id             */
+	N_("System Tray Icon"),                           /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Displays an icon for Gaim in the system tray."),
+	                                                  /**  description    */
+	N_("Interacts with a Notification Area applet (in GNOME or KDE, "
+	   "for example) to display the current status of Gaim, allow fast "
+	   "access to commonly used functions, and to toggle display of the "
+	   "buddy list or login window. Also allows messages to be queued "
+	   "until the icon is clicked, similar to ICQ."),
+	"Robert McQueen <robot101@debian.org>",           /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	&ui_info,                                         /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
 
-char *name() {
-	return _("System Tray Icon");
-}
-
-char *description() {
-	return _("Interacts with a Notification Area applet (in GNOME or KDE, for example) to display the current status of Gaim, allow fast access to commonly used functions, and to toggle display of the buddy list or login window. Also allows messages to be queued until the icon is clicked, similar to ICQ.");
-}
+GAIM_INIT_PLUGIN(docklet, __init_plugin, info);
--- a/plugins/error.c	Thu Apr 24 03:52:25 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-#define GAIM_PLUGINS
-#include "gaim.h"
-
-#include <stdlib.h>
-#include <time.h>
-
-char *gaim_plugin_error(int);
-
-char *gaim_plugin_init(GModule *handle) {
-	int error;
-
-	/* so here, we load any callbacks, do the normal stuff */
-
-	srand(time(NULL));
-	error = rand() % 3;
-	error -= 2;
-	/* there's a 1 in 3 chance there *won't* be an error :) */
-	return gaim_plugin_error(error);
-}
-
-void gaim_plugin_remove() {
-	/* this only gets called if we get loaded successfully, and then
-	 * unloaded. */
-}
-
-char *gaim_plugin_error(int error) {
-	/* by the time we've gotten here, all our callbacks are removed.
-	 * we just have to deal with what the error was (as defined by us)
-	 * and do any other clean-up stuff we need to do. */
-	switch (error) {
-	case -1:
-		return "MY BAD";
-	case -2:
-		return "Internal plugin error: exiting.";
-	default:
-		return NULL;
-	}
-}
-
-struct gaim_plugin_description desc; 
-struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup("Error Tester");
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup("A plugin that causes error messages.");
-	desc.authors = g_strdup("Eric Warmehoven &lt;eric@warmenhoven.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
-}
-
-char *name() {
-	return "Error Tester " VERSION ;
-}
-
-char *description() {
-	return "A nice little program that causes error messages";
-}
--- a/plugins/events.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/events.c	Fri Apr 25 06:47:33 2003 +0000
@@ -207,7 +207,7 @@
 
 struct gaim_plugin_description desc; 
 struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
+	desc.api_version = GAIM_PLUGIN_API_VERSION;
 	desc.name = g_strdup("Event Tester");
 	desc.version = g_strdup(VERSION);
 	desc.description = g_strdup("Test to see that all plugin events are working properly.");
--- a/plugins/filectl.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/filectl.c	Fri Apr 25 06:47:33 2003 +0000
@@ -120,7 +120,7 @@
 
 struct gaim_plugin_description desc; 
 struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
+	desc.api_version = GAIM_PLUGIN_API_VERSION;
 	desc.name = g_strdup("Gaim File Control");
 	desc.version = g_strdup(VERSION);
 	desc.description = g_strdup("Allows you to control Gaim by entering commands in aa file.");
--- a/plugins/gaiminc.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/gaiminc.c	Fri Apr 25 06:47:33 2003 +0000
@@ -55,7 +55,7 @@
 
 struct gaim_plugin_description desc; 
 struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
+	desc.api_version = GAIM_PLUGIN_API_VERSION;
 	desc.name = g_strdup("Demonstration");
 	desc.version = g_strdup(VERSION);
 	desc.description = g_strdup(
--- a/plugins/gestures/gestures.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/gestures/gestures.c	Fri Apr 25 06:47:33 2003 +0000
@@ -20,15 +20,12 @@
  */
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
 #include "gstroke.h"
+#include "gtkconv.h"
+#include "gtkplugin.h"
 
-static GModule *handle = NULL;
-struct gaim_plugin_description desc;
+#define GESTURES_PLUGIN_ID "gtk-gestures"
 
 static void
 stroke_close(GtkWidget *widget, void *data)
@@ -153,14 +150,12 @@
 	gstroke_set_draw_strokes(!gstroke_draw_strokes());
 }
 
-char *
-gaim_plugin_init(GModule *h)
+static gboolean
+plugin_load(GaimPlugin *plugin)
 {
 	struct gaim_conversation *conv;
 	GList *l;
 
-	handle = h;
-
 	for (l = gaim_get_conversations(); l != NULL; l = l->next) {
 		conv = (struct gaim_conversation *)l->data;
 
@@ -170,13 +165,13 @@
 		attach_signals(conv);
 	}
 
-	gaim_signal_connect(handle, event_new_conversation, new_conv_cb, NULL);
+	gaim_signal_connect(plugin, event_new_conversation, new_conv_cb, NULL);
 
-	return NULL;
+	return TRUE;
 }
 
-void
-gaim_plugin_remove(void)
+static gboolean
+plugin_unload(GaimPlugin *plugin)
 {
 	struct gaim_conversation *conv;
 	struct gaim_gtk_conversation *gtkconv;
@@ -192,10 +187,12 @@
 
 		gstroke_cleanup(gtkconv->imhtml);
 	}
+
+	return TRUE;
 }
 
-GtkWidget *
-gaim_plugin_config_gtk(void)
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
 {
 	GtkWidget *ret;
 	GtkWidget *vbox;
@@ -246,40 +243,47 @@
 	return ret;
 }
 
-struct gaim_plugin_description *
-gaim_plugin_desc()
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame
+};
+
+static GaimPluginInfo info =
 {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Mouse Gestures"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(
-		_("Allows support for mouse gestures in conversation windows.\n"
-		  "Drag the middle mouse button to perform certain actions:\n\n"
-		  "Drag down and then to the right to close a conversation.\n"
-		  "Drag up and then to the left to switch to the previous "
-		  "conversation.\n"
-		  "Drag up and then to the right to switch to the next "
-		  "conversation."));
-	desc.authors = g_strdup("Christian Hammond &lt;chipx86@gnupdate.org&gt;");
-	desc.url = g_strdup(WEBSITE);
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
 
-	return &desc;
+	GESTURES_PLUGIN_ID,                               /**< id             */
+	N_("Mouse Gestures"),                             /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Provides support for mouse gestures"),
+	                                                  /**  description    */
+	N_("Allows support for mouse gestures in conversation windows.\n"
+	   "Drag the middle mouse button to perform certain actions:\n\n"
+	   "Drag down and then to the right to close a conversation.\n"
+	   "Drag up and then to the left to switch to the previous "
+	   "conversation.\n"
+	   "Drag up and then to the right to switch to the next "
+	   "conversation."),
+	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	&ui_info,                                         /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
 
-char *
-name(void)
-{
-	return _("Mouse Gestures");
-}
-
-char *
-description(void)
-{
-	return _("Allows support for mouse gestures in conversation windows.\n"
-			 "Drag the middle mouse button to perform certain actions:\n\n"
-			 "Drag down and then to the right to close a conversation.\n"
-			 "Drag up and then to the left to switch to the previous "
-			 "conversation.\n"
-			 "Drag up and then to the right to switch to the next "
-			 "conversation.");
-}
+GAIM_INIT_PLUGIN(gestures, __init_plugin, info);
--- a/plugins/gtik.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/gtik.c	Fri Apr 25 06:47:33 2003 +0000
@@ -139,7 +139,7 @@
 		if (drawTimeID > 0) { g_source_remove(drawTimeID); }
 		if (updateTimeID >0) { g_source_remove(updateTimeID); }
 		gtk_widget_destroy(applet);
-		gaim_plugin_unload(handle);
+		gaim_plugin_unload_self(handle);
 	}
 
 
@@ -596,7 +596,7 @@
 	
         struct gaim_plugin_description desc;  
         struct gaim_plugin_description *gaim_plugin_desc() {
-		desc.api_version = PLUGIN_API_VERSION;
+		desc.api_version = GAIM_PLUGIN_API_VERSION;
 		desc.name = g_strdup("Stock Ticker");
 		desc.version = g_strdup(VERSION);
 		desc.description = g_strdup(
--- a/plugins/history.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/history.c	Fri Apr 25 06:47:33 2003 +0000
@@ -3,19 +3,16 @@
 
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
 #include "gtkimhtml.h"
+#include "gtkplugin.h"
 #include <sys/stat.h>
 #include <unistd.h>
 #include <string.h>
 
-#define HISTORY_SIZE (4 * 1024)
+#define HISTORY_PLUGIN_ID "core-history"
 
-GModule *handle;
+#define HISTORY_SIZE (4 * 1024)
 
 void historize (char *name, void *data)
 {
@@ -30,7 +27,7 @@
 	char *tmp;
 	int size;
 	GtkIMHtmlOptions options = GTK_IMHTML_NO_COLOURS;
-	
+
 	if (stat(path, &st) || S_ISDIR(st.st_mode) || st.st_size == 0 || 
 	    !(fd = fopen(path, "r"))) {
 		g_free(userdir);
@@ -38,12 +35,12 @@
 		g_free(path);
 		return;
 	}
-      
+
 	fseek(fd, st.st_size > HISTORY_SIZE ? st.st_size - HISTORY_SIZE : 0, SEEK_SET);
 	size = fread(buf, 1, HISTORY_SIZE, fd);
 	tmp = buf;
 	tmp[size] = 0;
-	
+
 	/* start the history at a newline */
 	while (*tmp && *tmp != '\n')
 		tmp++;
@@ -62,21 +59,39 @@
 	g_free(path);
 }
 
-G_MODULE_EXPORT char *gaim_plugin_init(GModule *h) {
-	handle = h;
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	gaim_signal_connect(plugin, event_new_conversation, historize, NULL);
 
-	gaim_signal_connect(handle, event_new_conversation, historize, NULL);
-
-	return NULL;
+	return TRUE;
 }
 
-struct gaim_plugin_description desc; 
-G_MODULE_EXPORT struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("History"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Shows recently logged conversations in new conversations "));
-	desc.authors = g_strdup("Sean Egan &lt;bj91704@binghamton.edu>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
+static GaimPluginInfo info =
+{
+	2,
+	GAIM_PLUGIN_STANDARD,
+	GAIM_GTK_PLUGIN_TYPE,
+	0,
+	NULL,
+	GAIM_PRIORITY_DEFAULT,
+	HISTORY_PLUGIN_ID,
+	N_("History"),
+	VERSION,
+	N_("Shows recently logged conversations in new conversations."),
+	N_("When a new conversation is opened this plugin will insert the last XXX of the last conversation into the current conversation."),
+	"Sean Egan <bj91704@binghamton.edu>",
+	WEBSITE,
+	plugin_load,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
+
+GAIM_INIT_PLUGIN(history, __init_plugin, info);
--- a/plugins/iconaway.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/iconaway.c	Fri Apr 25 06:47:33 2003 +0000
@@ -1,18 +1,13 @@
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
-
-#include <gtk/gtk.h>
+#include "gtkplugin.h"
 
 #ifdef _WIN32
 #include "win32dep.h"
 #endif
 
-void *handle;
+#define ICONAWAY_PLUGIN_ID "gtk-iconaway"
 
 G_MODULE_IMPORT GtkWidget *imaway;
 /*G_MODULE_IMPORT GtkWidget *blist;*/
@@ -54,29 +49,49 @@
  *  EXPORTED FUNCTIONS
  */
 
-G_MODULE_EXPORT char *gaim_plugin_init(GModule *h) {
-	handle = h;
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	gaim_signal_connect(plugin, event_away, iconify_windows, NULL);
 
-	gaim_signal_connect(handle, event_away, iconify_windows, NULL);
-
-	return NULL;
+	return TRUE;
 }
 
-struct gaim_plugin_description desc; 
-G_MODULE_EXPORT struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Iconify on away"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Iconifies the away box and the buddy list when you go away."));
-	desc.authors = g_strdup("Eric Warmenhoven &lt;eric@warmenhoven.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
-}
- 
-G_MODULE_EXPORT char *name() {
-	return _("Iconify on away");
+static GaimGtkPluginUiInfo ui_info =
+{
+	NULL                                            /**< get_config_frame */
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	ICONAWAY_PLUGIN_ID,                               /**< id             */
+	N_("Iconify on Away"),                            /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Iconifies the away box and the buddy list when you go away."),
+	                                                  /**  description    */
+	N_("Iconifies the away box and the buddy list when you go away."),
+	"Eric Warmenhoven <eric@warmenhoven.org>",        /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	&ui_info,                                         /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
 
-G_MODULE_EXPORT char *description() {
-	return _("Iconifies the away box and the buddy list when you go away.");
-}
+GAIM_INIT_PLUGIN(iconaway, __init_plugin, info);
--- a/plugins/idle.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/idle.c	Fri Apr 25 06:47:33 2003 +0000
@@ -5,28 +5,15 @@
 
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
 #include "multi.h"
+#include "gtkplugin.h"
 #include <sys/time.h>
 
+#define IDLE_PLUGIN_ID "gtk-idle"
+
 static struct gaim_connection *gc = NULL;
 
-const char *name() {
-	return _("I'dle Mak'er");
-}
-
-const char *description() {
-	return _("Allows you to hand-configure how long you've been idle for");
-}
-
-char *gaim_plugin_init(GModule *module) {
-	return NULL;
-}
-
 static void set_idle(GtkWidget *button, GtkWidget *spinner) {
 	time_t t;
 	int tm = CLAMP(gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner)), 0, G_MAXINT);
@@ -75,18 +62,9 @@
 		gc = NULL;
 }
 
-struct gaim_plugin_description desc;
-struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("I'dle Mak'er"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Allows you to hand-configure how long you've been idle for"));
-	desc.authors = g_strdup("Eric Warmenhoven &lt;eric@warmenhoven.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
-}
-
-GtkWidget *gaim_plugin_config_gtk() {
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
+{
 	GtkWidget *ret;
 	GtkWidget *frame, *label;
 	GtkWidget *vbox, *hbox;
@@ -130,3 +108,37 @@
 
 	return ret;
 }
+
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame
+};
+
+static GaimPluginInfo info =
+{
+	2,
+	GAIM_PLUGIN_STANDARD,
+	GAIM_GTK_PLUGIN_TYPE,
+	0,
+	NULL,
+	GAIM_PRIORITY_DEFAULT,
+	IDLE_PLUGIN_ID,
+	N_("I'dle Mak'er"),
+	VERSION,
+	N_("Allows you to hand-configure how long you've been idle for"),
+	N_("Allows you to hand-configure how long you've been idle for"),
+	"Eric Warmenhoven &lt;eric@warmenhoven.org>",
+	WEBSITE,
+	NULL,
+	NULL,
+	NULL,
+	&ui_info,
+	NULL
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+}
+
+GAIM_INIT_PLUGIN(idle, __init_plugin, info);
--- a/plugins/mailchk.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/mailchk.c	Fri Apr 25 06:47:33 2003 +0000
@@ -1,15 +1,11 @@
-#include "config.h"
-
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
 #include "sound.h"
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#define MAILCHK_PLUGIN_ID "core-mailchk"
+
 #define ANY_MAIL    0x01
 #define UNREAD_MAIL 0x02
 #define NEW_MAIL    0x04
@@ -122,7 +118,7 @@
 
 struct gaim_plugin_description desc; 
 struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
+	desc.api_version = GAIM_PLUGIN_API_VERSION;
 	desc.name = g_strdup("Mail Checker");
 	desc.version = g_strdup(VERSION);
 	desc.description = g_strdup("Checks for new local mail.");
--- a/plugins/notify.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/notify.c	Fri Apr 25 06:47:33 2003 +0000
@@ -20,10 +20,6 @@
 
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
 #include <string.h>
 #include <ctype.h>
@@ -33,6 +29,9 @@
 #include <X11/Xutil.h>
 #include <X11/Xatom.h>
 #include <gdk/gdkx.h>
+#include "gtkplugin.h"
+
+#define NOTIFY_PLUGIN_ID "gtk-notify"
 
 guint type = 1;
 #define TYPE_IM				0x00000001
@@ -558,60 +557,9 @@
 	}
 }
 
-char *gaim_plugin_init(GModule *hndl) {
-	handle = hndl;
-	title_string = g_strdup("(*) ");
-
-	load_notify_prefs();
-
-	gaim_signal_connect(handle, event_im_recv, im_recv_im, NULL);
-	gaim_signal_connect(handle, event_chat_recv, chat_recv_im, NULL);
-	gaim_signal_connect(handle, event_im_send, im_sent_im, NULL);
-	gaim_signal_connect(handle, event_chat_send, chat_sent_im, NULL);
-	gaim_signal_connect(handle, event_new_conversation, new_conv, NULL);
-	gaim_signal_connect(handle, event_chat_join, chat_join, NULL);
-	return NULL;
-}
-
-void gaim_plugin_remove() {
-	GList *c = gaim_get_conversations();
-
-	while (c) {
-		struct gaim_conversation *cnv = (struct gaim_conversation *)c->data;
-		struct gaim_gtk_window *gtkwin;
-
-		gtkwin = GAIM_GTK_WINDOW(gaim_conversation_get_window(cnv));
-
-		detach_signals(cnv);
-		un_star(gtkwin->window, NULL);
-
-		c = c->next;
-	}
-	
-	/* this might be a hack I'm not sure, I don't think so but... */
-	g_free(title_string);
-}
-
-struct gaim_plugin_description desc; 
-struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Message Notification"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Provides a variety of ways of notifying you of unread messages."));
-	desc.authors = g_strdup("Etan Reisner &lt;deryni@eden.rutgers.edu>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
-}
-
-char *name() {
-	return _("Message Notification");
-}
-
-char *description() {
-	return _("Provides a variety of ways of notifying you of unread messages.");
-}
-
-GtkWidget *gaim_plugin_config_gtk() {
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
+{
 	GtkWidget *ret;
 	GtkWidget *vbox, *hbox;
 	GtkWidget *toggle, *button;
@@ -688,3 +636,82 @@
 	gtk_widget_show_all(ret);
 	return ret;
 }
+
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	title_string = g_strdup("(*) ");
+
+	load_notify_prefs();
+
+	gaim_signal_connect(plugin, event_im_recv, im_recv_im, NULL);
+	gaim_signal_connect(plugin, event_chat_recv, chat_recv_im, NULL);
+	gaim_signal_connect(plugin, event_im_send, im_sent_im, NULL);
+	gaim_signal_connect(plugin, event_chat_send, chat_sent_im, NULL);
+	gaim_signal_connect(plugin, event_new_conversation, new_conv, NULL);
+	gaim_signal_connect(plugin, event_chat_join, chat_join, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+	GList *c = gaim_get_conversations();
+
+	while (c) {
+		struct gaim_conversation *cnv = (struct gaim_conversation *)c->data;
+		struct gaim_gtk_window *gtkwin;
+
+		gtkwin = GAIM_GTK_WINDOW(gaim_conversation_get_window(cnv));
+
+		detach_signals(cnv);
+		un_star(gtkwin->window, NULL);
+
+		c = c->next;
+	}
+	
+	/* this might be a hack I'm not sure, I don't think so but... */
+	g_free(title_string);
+
+	return TRUE;
+}
+
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	NOTIFY_PLUGIN_ID,                                 /**< id             */
+	N_("Message Notification"),                       /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Provides a variety of ways of notifying you of unread messages."),
+	                                                  /**  description    */
+	N_("Provides a variety of ways of notifying you of unread messages."),
+	"Etan Reisner <deryni@eden.rutgers.edu>",         /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	&ui_info,                                         /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+}
+
+GAIM_INIT_PLUGIN(notify, __init_plugin, info);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/perl/.cvsignore	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.dll
+*.la
+*.lo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/perl/Makefile.am	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,14 @@
+plugindir = $(libdir)/gaim
+
+perl_la_LDFLAGS = -module -avoid-version $(PERL_LIBS)
+
+plugin_LTLIBRARIES = perl.la
+
+perl_la_SOURCES      = perl.c
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-DVERSION=\"$(VERSION)\" \
+	$(DEBUG_CFLAGS) \
+	$(PLUGIN_CFLAGS) \
+	$(PERL_CFLAGS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/perl/perl.c	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,1390 @@
+/*
+ * 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
+ *
+ * This was taken almost exactly from X-Chat. The power of the GPL.
+ * Translated from X-Chat to Gaim by Eric Warmenhoven.
+ * Originally by Erik Scrafford <eriks@chilisoft.com>.
+ * X-Chat Copyright (C) 1998 Peter Zelezny.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#endif
+#undef PACKAGE
+
+#define group perl_group
+#ifdef _WIN32
+/* This took me an age to figure out.. without this __declspec(dllimport)
+ * will be ignored.
+ */
+#define HASATTRIBUTE
+#endif
+#include <EXTERN.h>
+#ifndef _SEM_SEMUN_UNDEFINED
+#define HAS_UNION_SEMUN
+#endif
+#include <perl.h>
+#include <XSUB.h>
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#undef PACKAGE
+#include <stdio.h>
+#ifndef _WIN32
+#include <dirent.h>
+#else
+/* We're using perl's win32 port of this */
+#define dirent direct
+#endif
+#include <string.h>
+
+#undef group
+
+/* perl module support */
+#ifdef OLD_PERL
+extern void boot_DynaLoader _((CV * cv));
+#else
+extern void boot_DynaLoader _((pTHX_ CV * cv)); /* perl is so wacky */
+#endif
+
+#undef _
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#ifdef _WIN32
+#undef pipe
+#endif
+#include "gaim.h"
+#include "prpl.h"
+#include "sound.h"
+
+#ifndef call_pv
+# define call_pv(i,j) perl_call_pv((i), (j))
+#endif
+
+#define PERL_PLUGIN_ID "core-perl"
+
+struct perlscript {
+	char *name;
+	char *version;
+	char *shutdowncallback; /* bleh */
+	GaimPlugin *plug;
+};
+
+struct _perl_event_handlers {
+	char *event_type;
+	char *handler_name;
+	GaimPlugin *plug;
+};
+
+struct _perl_timeout_handlers {
+	char *handler_name;
+	char *handler_args;
+	gint iotag;
+	GaimPlugin *plug;
+};
+
+static GList *perl_list = NULL;
+static GList *perl_timeout_handlers = NULL;
+static GList *perl_event_handlers = NULL;
+static PerlInterpreter *my_perl = NULL;
+static void perl_init();
+
+/* dealing with gaim */
+XS(XS_GAIM_register); /* set up hooks for script */
+XS(XS_GAIM_get_info); /* version, last to attempt signon, protocol */
+XS(XS_GAIM_print); /* lemme figure this one out... */
+XS(XS_GAIM_write_to_conv); /* write into conversation window */
+
+/* list stuff */
+XS(XS_GAIM_buddy_list); /* all buddies */
+XS(XS_GAIM_online_list); /* online buddies */
+
+/* server stuff */
+XS(XS_GAIM_command); /* send command to server */
+XS(XS_GAIM_user_info); /* given name, return struct buddy members */
+XS(XS_GAIM_print_to_conv); /* send message to someone */
+XS(XS_GAIM_print_to_chat); /* send message to chat room */
+XS(XS_GAIM_serv_send_im); /* send message to someone (but do not display) */
+
+/* handler commands */
+XS(XS_GAIM_add_event_handler); /* when servers talk */
+XS(XS_GAIM_remove_event_handler); /* remove a handler */
+XS(XS_GAIM_add_timeout_handler); /* figure it out */
+
+/* play sound */
+XS(XS_GAIM_play_sound); /*play a sound */
+
+static void
+#ifdef OLD_PERL
+xs_init()
+#else
+xs_init(pTHX)
+#endif
+{
+	char *file = __FILE__;
+
+	/* This one allows dynamic loading of perl modules in perl
+           scripts by the 'use perlmod;' construction*/
+	newXS ("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+
+	/* load up all the custom Gaim perl functions */
+	newXS ("GAIM::register", XS_GAIM_register, "GAIM");
+	newXS ("GAIM::get_info", XS_GAIM_get_info, "GAIM");
+	newXS ("GAIM::print", XS_GAIM_print, "GAIM");
+	newXS ("GAIM::write_to_conv", XS_GAIM_write_to_conv, "GAIM");
+
+	newXS ("GAIM::buddy_list", XS_GAIM_buddy_list, "GAIM");
+	newXS ("GAIM::online_list", XS_GAIM_online_list, "GAIM");
+
+	newXS ("GAIM::command", XS_GAIM_command, "GAIM");
+	newXS ("GAIM::user_info", XS_GAIM_user_info, "GAIM");
+	newXS ("GAIM::print_to_conv", XS_GAIM_print_to_conv, "GAIM");
+	newXS ("GAIM::print_to_chat", XS_GAIM_print_to_chat, "GAIM");
+	newXS ("GAIM::serv_send_im", XS_GAIM_serv_send_im, "GAIM");
+
+	newXS ("GAIM::add_event_handler", XS_GAIM_add_event_handler, "GAIM");
+	newXS ("GAIM::remove_event_handler", XS_GAIM_remove_event_handler, "GAIM");
+	newXS ("GAIM::add_timeout_handler", XS_GAIM_add_timeout_handler, "GAIM");
+
+	newXS ("GAIM::play_sound", XS_GAIM_play_sound, "GAIM");
+}
+
+static char *
+escape_quotes(const char *buf)
+{
+	static char *tmp_buf = NULL;
+	const char *i;
+	char *j;
+
+	if (tmp_buf)
+		g_free(tmp_buf);
+
+	tmp_buf = g_malloc(strlen(buf) * 2 + 1);
+
+	for (i = buf, j = tmp_buf; *i; i++, j++) {
+		if (*i == '\'' || *i == '\\')
+			*j++ = '\\';
+
+		*j = *i;
+	}
+
+	*j = '\0';
+
+	return tmp_buf;
+}
+
+/*
+  2003/02/06: execute_perl modified by Mark Doliner <mark@kingant.net>
+		Pass parameters by pushing them onto the stack rather than 
+		passing an array of strings.  This way, perl scripts can 
+		modify the parameters and we can get the changed values 
+		and then shoot ourselves.  I mean, uh, use them.
+
+  2001/06/14: execute_perl replaced by Martin Persson <mep@passagen.se>
+		previous use of perl_eval leaked memory, replaced with
+		a version that uses perl_call instead
+
+  30/11/2002: execute_perl modified by Eric Timme <timothy@voidnet.com>
+		args changed to char** so that we can have preparsed
+  		arguments again, and many headaches ensued! This essentially 
+		means we replaced one hacked method with a messier hacked 
+		method out of perceived necessity. Formerly execute_perl 
+		required a single char_ptr, and it would insert it into an 
+		array of character pointers and NULL terminate the new array.
+		Now we have to pass in pre-terminated character pointer arrays
+		to accomodate functions that want to pass in multiple arguments.
+
+		Previously arguments were preparsed because an argument list
+		was constructed in the form 'arg one','arg two' and was
+		executed via a call like &funcname(arglist) (see .59.x), so
+		the arglist was magically pre-parsed because of the method. 
+		With Martin Persson's change to perl_call we now need to
+		use a null terminated list of character pointers for arguments
+		if we wish them to be parsed. Lacking a better way to allow
+		for both single arguments and many I created a NULL terminated
+		array in every function that called execute_perl and passed
+		that list into the function.  In the former version a single
+		character pointer was passed in, and was placed into an array
+		of character pointers with two elements, with a NULL element
+		tacked onto the back, but this method no longer seemed prudent.
+
+		Enhancements in the future might be to get rid of pre-declaring
+		the array sizes?  I am not comfortable enough with this
+		subject to attempt it myself and hope it to stand the test
+		of time.
+*/
+
+static int 
+execute_perl(const char *function, int argc, char **args)
+{
+	int count = 0, i, ret_value = 1;
+	SV *sv_args[argc];
+	STRLEN na;
+
+	/*
+	 * Set up the perl environment, push arguments onto the 
+	 * perl stack, then call the given function
+	 */
+	dSP;
+	ENTER;
+	SAVETMPS;
+	PUSHMARK(sp);
+
+	for (i = 0; i < argc; i++) {
+		if (args[i]) {
+			sv_args[i] = sv_2mortal(newSVpv(args[i], 0));
+			XPUSHs(sv_args[i]);
+		}
+	}
+
+	PUTBACK;
+	count = call_pv(function, G_EVAL | G_SCALAR);
+	SPAGAIN;
+
+	/*
+	 * Check for "die," make sure we have 1 argument, and set our
+	 * return value.
+	 */
+	if (SvTRUE(ERRSV)) {
+		debug_printf("Perl function %s exited abnormally: %s\n",
+					 function, SvPV(ERRSV, na));
+		POPs;
+	}
+	else if (count != 1) {
+		/*
+		 * This should NEVER happen.  G_SCALAR ensures that we WILL
+		 * have 1 parameter.
+		 */
+		debug_printf("Perl error from %s: expected 1 return value, "
+					 "but got %d\n", function, count);
+	}
+	else
+		ret_value = POPi;
+
+	/* Check for changed arguments */
+	for (i = 0; i < argc; i++) {
+		if (args[i] && strcmp(args[i], SvPVX(sv_args[i]))) {
+			/*
+			 * Shizzel.  So the perl script changed one of the parameters,
+			 * and we want this change to affect the original parameters.
+			 * args[i] is just a tempory little list of pointers.  We don't
+			 * want to free args[i] here because the new parameter doesn't
+			 * overwrite the data that args[i] points to.  That is done by
+			 * the function that called execute_perl.  I'm not explaining this
+			 * very well.  See, it's aggregate...  Oh, but if 2 perl scripts
+			 * both modify the data, _that's_ a memleak.  This is really kind
+			 * of hackish.  I should fix it.  Look how long this comment is.
+			 * Holy crap.
+			 */
+			args[i] = g_strdup(SvPV(sv_args[i], na));
+		}
+	}
+
+	PUTBACK;
+	FREETMPS;
+	LEAVE;
+
+	return ret_value;
+}
+
+static void
+perl_unload_file(GaimPlugin *plug)
+{
+	char *atmp[2] = { "", NULL };
+	struct perlscript *scp = NULL;
+	struct _perl_timeout_handlers *thn;
+	struct _perl_event_handlers *ehn;
+	GList *pl;
+
+	for (pl = perl_list; pl != NULL; pl = pl->next) {
+		scp = pl->data;
+
+		if (scp->plug == plug) {
+			perl_list = g_list_remove(perl_list, scp);
+
+			if (scp->shutdowncallback[0])
+				execute_perl(scp->shutdowncallback, 1, atmp);
+
+			g_free(scp->name);
+			g_free(scp->version);
+			g_free(scp->shutdowncallback);
+			g_free(scp);	
+
+			break;
+		}
+	}
+
+	for (pl = perl_timeout_handlers; pl != NULL; pl = pl->next) {
+		thn = pl->data;
+
+		if (thn && thn->plug == plug) {
+			perl_timeout_handlers = g_list_remove(perl_timeout_handlers, thn);
+
+			g_source_remove(thn->iotag);
+			g_free(thn->handler_args);
+			g_free(thn->handler_name);
+			g_free(thn);
+		}
+	}
+
+	for (pl = perl_event_handlers; pl != NULL; pl = pl->next) {
+		ehn = pl->data;
+
+		if (ehn && ehn->plug == plug) {
+			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
+
+			g_free(ehn->event_type);
+			g_free(ehn->handler_name);
+			g_free(ehn);
+		}
+	}
+}
+
+static int
+perl_load_file(char *script_name, GaimPlugin *plugin)
+{
+	char *atmp[2] = { script_name, NULL };
+	GList *s;
+	struct perlscript *scp;
+	int ret;
+
+	if (my_perl == NULL)
+		perl_init();
+
+	plugin->handle = plugin->path;
+
+	ret = execute_perl("load_n_eval", 1, atmp);
+
+	for (s = perl_list; s != NULL; s = s->next) {
+		scp = s->data;
+
+		if (!strcmp(scp->name, plugin->info->name) &&
+		    !strcmp(scp->version, plugin->info->version)) {
+
+			break;
+		}
+	}
+
+	if (!s) {
+		plugin->error = g_strdup(_("GAIM::register not called with "
+								   "proper arguments.  Consult PERL-HOWTO."));
+
+		return 0;
+	}
+	
+	return ret;
+}
+
+static void
+perl_init(void)
+{
+	/* changed the name of the variable from load_file to
+	   perl_definitions since now it does much more than defining
+	   the load_file sub. Moreover, deplaced the initialisation to
+	   the xs_init function. (TheHobbit)*/
+	char *perl_args[] = { "", "-e", "0", "-w" };
+	char perl_definitions[] =
+	{
+		/* We use to function one to load a file the other to
+		   execute the string obtained from the first and holding
+		   the file conents. This allows to have a realy local $/
+		   without introducing temp variables to hold the old
+		   value. Just a question of style:) */ 
+		"sub load_file{"
+		  "my $f_name=shift;"
+		  "local $/=undef;"
+		  "open FH,$f_name or return \"__FAILED__\";"
+		  "$_=<FH>;"
+		  "close FH;"
+		  "return $_;"
+		"}"
+		"sub load_n_eval{"
+		  "my $f_name=shift;"
+		  "my $strin=load_file($f_name);"
+		  "return 2 if($strin eq \"__FAILED__\");"
+		  "eval $strin;"
+		  "if($@){"
+		    /*"  #something went wrong\n"*/
+		    "GAIM::print(\"Errors loading file $f_name:\\n\",\"$@\");"
+		    "return 1;"
+		  "}"
+		  "return 0;"
+		"}"
+	};
+
+	my_perl = perl_alloc();
+	perl_construct(my_perl);
+#ifdef DEBUG
+	perl_parse(my_perl, xs_init, 4, perl_args, NULL);
+#else
+	perl_parse(my_perl, xs_init, 3, perl_args, NULL);
+#endif
+#ifdef HAVE_PERL_EVAL_PV
+	eval_pv(perl_definitions, TRUE);
+#else
+	perl_eval_pv(perl_definitions, TRUE); /* deprecated */
+#endif
+}
+
+static void
+perl_end(void)
+{
+	char *atmp[2] = { "", NULL };
+	struct perlscript *scp;
+	struct _perl_timeout_handlers *thn;
+	struct _perl_event_handlers *ehn;
+
+	while (perl_list) {
+		scp = perl_list->data;
+		perl_list = g_list_remove(perl_list, scp);
+
+		if (scp->shutdowncallback[0])
+			execute_perl(scp->shutdowncallback, 1, atmp);
+
+		g_free(scp->name);
+		g_free(scp->version);
+		g_free(scp->shutdowncallback);
+		g_free(scp);
+	}
+
+	while (perl_timeout_handlers) {
+		thn = perl_timeout_handlers->data;
+		perl_timeout_handlers = g_list_remove(perl_timeout_handlers, thn);
+		g_source_remove(thn->iotag);
+		g_free(thn->handler_args);
+		g_free(thn->handler_name);
+		g_free(thn);
+	}
+
+	while (perl_event_handlers) {
+		ehn = perl_event_handlers->data;
+		perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
+		g_free(ehn->event_type);
+		g_free(ehn->handler_name);
+		g_free(ehn);
+	}
+
+	if (my_perl != NULL) {
+		perl_destruct(my_perl);
+		perl_free(my_perl);
+		my_perl = NULL;
+	}
+}
+
+XS (XS_GAIM_register)
+{
+	char *name, *ver, *callback, *unused; /* exactly like X-Chat, eh? :) */
+	unsigned int junk;
+	struct perlscript *scp;
+	GaimPlugin *plug = NULL;
+	GList *pl;
+
+	dXSARGS;
+	items = 0;
+
+	name     = SvPV(ST(0), junk);
+	ver      = SvPV(ST(1), junk);
+	callback = SvPV(ST(2), junk);
+	unused   = SvPV(ST(3), junk);
+
+	debug_printf("GAIM::register(%s, %s)\n", name, ver);
+
+	for (pl = gaim_plugins_get_all(); pl != NULL; pl = pl->next) {
+		plug = pl->data;
+
+		debug_printf("** Comparing '%s' to '%s' and '%s' to '%s'\n",
+					 name, plug->info->name, ver,
+					 plug->info->version);
+
+		if (!strcmp(name, plug->info->name) &&
+		    !strcmp(ver, plug->info->version)) {
+
+			break;
+		}
+
+		plug = NULL;
+	}
+
+	if (plug) {
+		scp = g_new0(struct perlscript, 1);
+		scp->name = g_strdup(name);
+		scp->version = g_strdup(ver);
+		scp->shutdowncallback = g_strdup(callback);
+		scp->plug = plug; 
+
+		perl_list = g_list_append(perl_list, scp);
+
+		XST_mPV(0, plug->path);
+	}
+	else
+		XST_mPV(0, NULL);
+
+	XSRETURN (1);
+}
+
+XS (XS_GAIM_get_info)
+{
+	int i = 0;
+	dXSARGS;
+	items = 0;
+
+	switch(SvIV(ST(0))) {
+		case 0:
+			XST_mPV(0, VERSION);
+			i = 1;
+			break;
+
+		case 1:
+			{
+				GSList *c = connections;
+				struct gaim_connection *gc;
+
+				while (c) {
+					gc = (struct gaim_connection *)c->data;
+					XST_mIV(i++, (guint)gc);
+					c = c->next;
+				}
+			}
+			break;
+
+		case 2:
+			{
+				struct gaim_connection *gc =
+					(struct gaim_connection *)SvIV(ST(1));
+
+				if (g_slist_find(connections, gc))
+					XST_mIV(i++, gc->protocol);
+				else
+					XST_mIV(i++, -1);
+			}
+			break;
+
+		case 3:
+			{
+				struct gaim_connection *gc =
+					(struct gaim_connection *)SvIV(ST(1));
+
+				if (g_slist_find(connections, gc))
+					XST_mPV(i++, gc->username);
+				else
+					XST_mPV(i++, "");
+			}
+			break;
+
+		case 4:
+			{
+				struct gaim_connection *gc =
+					(struct gaim_connection *)SvIV(ST(1));
+
+				if (g_slist_find(connections, gc))
+					XST_mIV(i++, g_slist_index(gaim_accounts, gc->account));
+				else
+					XST_mIV(i++, -1);
+			}
+			break;
+
+		case 5:
+			{
+				GSList *a = gaim_accounts;
+				while (a) {
+					struct gaim_account *account = a->data;
+					XST_mPV(i++, account->username);
+					a = a->next;
+				}
+			}
+			break;
+
+		case 6:
+			{
+				GSList *a = gaim_accounts;
+				while (a) {
+					struct gaim_account *account = a->data;
+					XST_mIV(i++, account->protocol);
+					a = a->next;
+				}
+			}
+			break;
+
+		case 7:
+			{
+				struct gaim_connection *gc =
+					(struct gaim_connection *)SvIV(ST(1));
+
+				if (g_slist_find(connections, gc))
+					XST_mPV(i++, gc->prpl->info->name);
+				else
+					XST_mPV(i++, "Unknown");
+			}
+			break;
+
+		default:
+			XST_mPV(0, "Error2");
+			i = 1;
+	}
+
+	XSRETURN(i);
+}
+
+XS (XS_GAIM_print)
+{
+	char *title;
+	char *message;
+	unsigned int junk;
+	dXSARGS;
+	items = 0;
+
+	title = SvPV(ST(0), junk);
+	message = SvPV(ST(1), junk);
+	do_error_dialog(title, message, GAIM_INFO);
+	XSRETURN(0);
+}
+
+XS (XS_GAIM_buddy_list)
+{
+	struct gaim_connection *gc;
+	struct buddy *buddy;
+	struct group *g;
+	GaimBlistNode *gnode,*bnode;
+	int i = 0;
+	dXSARGS;
+	items = 0;
+
+	gc = (struct gaim_connection *)SvIV(ST(0));
+
+	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
+		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		g = (struct group *)gnode;
+		for(bnode = gnode->child; bnode; bnode = bnode->next) {
+			if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+				continue;
+			buddy = (struct buddy *)bnode;
+			if(buddy->account == gc->account)
+				XST_mPV(i++, buddy->name);
+		}
+	}
+	XSRETURN(i);
+}
+
+XS (XS_GAIM_online_list)
+{
+	struct gaim_connection *gc;
+	struct buddy *b;
+	struct group *g;
+	GaimBlistNode *gnode,*bnode;
+	int i = 0;
+	dXSARGS;
+	items = 0;
+
+	gc = (struct gaim_connection *)SvIV(ST(0));
+
+	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
+		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		g = (struct group *)gnode;
+		for(bnode = gnode->child; bnode; bnode = bnode->next) {
+			if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
+				continue;
+			b = (struct buddy *)bnode;
+			if (b->account == gc->account && GAIM_BUDDY_IS_ONLINE(b)) XST_mPV(i++, b->name);
+		}
+	}
+	XSRETURN(i);
+}
+
+XS (XS_GAIM_command)
+{
+	unsigned int junk;
+	char *command = NULL;
+	dXSARGS;
+	items = 0;
+
+	command = SvPV(ST(0), junk);
+	if (!command) XSRETURN(0);
+	if (!strncasecmp(command, "signon", 6)) {
+		int index = SvIV(ST(1));
+		if (g_slist_nth_data(gaim_accounts, index))
+		serv_login(g_slist_nth_data(gaim_accounts, index));
+	} else if (!strncasecmp(command, "signoff", 7)) {
+		struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
+		if (g_slist_find(connections, gc)) signoff(gc);
+		else signoff_all(NULL, NULL);
+	} else if (!strncasecmp(command, "info", 4)) {
+		struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
+		if (g_slist_find(connections, gc))
+			serv_set_info(gc, SvPV(ST(2), junk));
+	} else if (!strncasecmp(command, "away", 4)) {
+		char *message = SvPV(ST(1), junk);
+		static struct away_message a;
+		g_snprintf(a.message, sizeof(a.message), "%s", message);
+		do_away_message(NULL, &a);
+	} else if (!strncasecmp(command, "back", 4)) {
+		do_im_back();
+	} else if (!strncasecmp(command, "idle", 4)) {
+		GSList *c = connections;
+		struct gaim_connection *gc;
+
+		while (c) {
+			gc = (struct gaim_connection *)c->data;
+			serv_set_idle(gc, SvIV(ST(1)));
+			c = c->next;
+		}
+	} else if (!strncasecmp(command, "warn", 4)) {
+		GSList *c = connections;
+		struct gaim_connection *gc;
+
+		while (c) {
+			gc = (struct gaim_connection *)c->data;
+			serv_warn(gc, SvPV(ST(1), junk), SvIV(ST(2)));
+			c = c->next;
+		}
+	}
+
+	XSRETURN(0);
+}
+
+XS (XS_GAIM_user_info)
+{
+	struct gaim_connection *gc;
+	unsigned int junk;
+	struct buddy *buddy = NULL;
+	dXSARGS;
+	items = 0;
+
+	gc = (struct gaim_connection *)SvIV(ST(0));
+	if (g_slist_find(connections, gc))
+		buddy = gaim_find_buddy(gc->account, SvPV(ST(1), junk));
+
+	if (!buddy)
+		XSRETURN(0);
+	XST_mPV(0, buddy->name);
+	XST_mPV(1, gaim_get_buddy_alias(buddy));
+	XST_mPV(2, GAIM_BUDDY_IS_ONLINE(buddy) ? "Online" : "Offline");
+	XST_mIV(3, buddy->evil);
+	XST_mIV(4, buddy->signon);
+	XST_mIV(5, buddy->idle);
+	XSRETURN(6);
+}
+
+XS (XS_GAIM_write_to_conv)
+{
+	char *nick, *who, *what;
+	struct gaim_conversation *c;
+	int junk;
+	int send, wflags;
+	dXSARGS;
+	items = 0;
+
+	nick = SvPV(ST(0), junk);
+	send = SvIV(ST(1));
+	what = SvPV(ST(2), junk);
+	who = SvPV(ST(3), junk);
+	
+	if (!*who) who=NULL;
+	
+	switch (send) {
+		case 0: wflags=WFLAG_SEND; break;
+		case 1: wflags=WFLAG_RECV; break;
+		case 2: wflags=WFLAG_SYSTEM; break;
+		default: wflags=WFLAG_RECV;
+	}	
+
+	c = gaim_find_conversation(nick);
+
+	if (!c)
+		c = gaim_conversation_new(GAIM_CONV_IM, NULL, nick);
+
+	gaim_conversation_write(c, who, what, -1, wflags, time(NULL));
+	XSRETURN(0);
+}
+
+XS (XS_GAIM_serv_send_im)
+{
+	struct gaim_connection *gc;
+	char *nick, *what;
+	int isauto;
+	int junk;
+	dXSARGS;
+	items = 0;
+
+	gc = (struct gaim_connection *)SvIV(ST(0));
+	nick = SvPV(ST(1), junk);
+	what = SvPV(ST(2), junk);
+	isauto = SvIV(ST(3));
+
+	if (!g_slist_find(connections, gc)) {
+		XSRETURN(0);
+		return;
+	}
+	serv_send_im(gc, nick, what, -1, isauto);
+	XSRETURN(0);
+}
+
+XS (XS_GAIM_print_to_conv)
+{
+	struct gaim_connection *gc;
+	char *nick, *what;
+	int isauto;
+	struct gaim_conversation *c;
+	unsigned int junk;
+	dXSARGS;
+	items = 0;
+
+	gc = (struct gaim_connection *)SvIV(ST(0));
+	nick = SvPV(ST(1), junk);
+	what = SvPV(ST(2), junk);
+	isauto = SvIV(ST(3));
+	if (!g_slist_find(connections, gc)) {
+		XSRETURN(0);
+		return;
+	}
+
+	c = gaim_find_conversation(nick);
+
+	if (!c)
+		c = gaim_conversation_new(GAIM_CONV_IM, gc->account, nick);
+	else
+		gaim_conversation_set_account(c, gc->account);
+
+	gaim_conversation_write(c, NULL, what, -1, 
+				(WFLAG_SEND | (isauto ? WFLAG_AUTO : 0)), time(NULL));
+	serv_send_im(gc, nick, what, -1, isauto ? IM_FLAG_AWAY : 0);
+	XSRETURN(0);
+}
+
+
+	
+XS (XS_GAIM_print_to_chat)
+{
+	struct gaim_connection *gc;
+	int id;
+	char *what;
+	struct gaim_conversation *b = NULL;
+	GSList *bcs;
+	unsigned int junk;
+	dXSARGS;
+	items = 0;
+
+	gc = (struct gaim_connection *)SvIV(ST(0));
+	id = SvIV(ST(1));
+	what = SvPV(ST(2), junk);
+
+	if (!g_slist_find(connections, gc)) {
+		XSRETURN(0);
+		return;
+	}
+	bcs = gc->buddy_chats;
+	while (bcs) {
+		b = (struct gaim_conversation *)bcs->data;
+
+		if (gaim_chat_get_id(gaim_conversation_get_chat_data(b)) == id)
+			break;
+		bcs = bcs->next;
+		b = NULL;
+	}
+	if (b)
+		serv_chat_send(gc, id, what);
+	XSRETURN(0);
+}
+
+static int
+perl_event(GaimEvent event, void *unused, va_list args)
+{
+	char *buf[5] = { NULL, NULL, NULL, NULL, NULL }; /* Maximum of 5 args */
+	void *arg1 = NULL, *arg2 = NULL, *arg3 = NULL, *arg4 = NULL, *arg5 = NULL;
+	char tmpbuf1[16], tmpbuf2[16], tmpbuf3[1];
+	GList *handler;
+	struct _perl_event_handlers *data;
+	int handler_return;
+
+	arg1 = va_arg(args, void *);
+	arg2 = va_arg(args, void *);
+	arg3 = va_arg(args, void *);
+	arg4 = va_arg(args, void *);
+	arg5 = va_arg(args, void *);
+
+	tmpbuf1[0] = '\0';
+	tmpbuf2[0] = '\0';
+	tmpbuf3[0] = '\0';
+
+	/* Make a pretty array of char*'s with which to call perl functions */
+	switch (event) {
+	case event_signon:
+	case event_signoff:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		break;
+	case event_away:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = ((struct gaim_connection *)arg1)->away ?
+				((struct gaim_connection *)arg1)->away : tmpbuf2;
+		break;
+	case event_im_recv:
+		if (!*(char**)arg2 || !*(char**)arg3) return 1;
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = *(char **)arg2;
+		buf[2] = *(char **)arg3;
+		break;
+	case event_im_send:
+		if (!*(char**)arg3) return 1;
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2 ? arg2 : tmpbuf3;
+		buf[2] = *(char **)arg3;
+		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:
+	case event_got_typing:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		break;
+	case event_chat_invited:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = arg3;
+		buf[3] = arg4;
+		break;
+	case event_chat_join:
+	case event_chat_buddy_join:
+	case event_chat_buddy_leave:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = arg3;
+		break;
+	case event_chat_leave:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		break;
+	case event_chat_recv:
+		if (!*(char**)arg3 || !*(char**)arg4) return 1;
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = *(char **)arg3;
+		buf[3] = *(char **)arg4;
+		break;
+	case event_chat_send_invite:
+		if (!*(char**)arg4) return 1;
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = arg3;
+		buf[3] = *(char **)arg4;
+		break;
+	case event_chat_send:
+		if (!*(char**)arg3) return 1;
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
+		buf[1] = tmpbuf2;
+		buf[2] = *(char **)arg3;
+		break;
+	case event_warned:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2 ? arg2 : tmpbuf3;
+		g_snprintf(tmpbuf2, 16, "%d", (int)arg3);
+		buf[2] = tmpbuf2;
+		break;
+	case event_quit:
+	case event_blist_update:
+		buf[0] = tmpbuf3;
+		break;
+	case event_new_conversation:
+	case event_del_conversation:
+		buf[0] = arg1;
+		break;
+	case event_im_displayed_sent:
+		if (!*(char**)arg3) return 1;
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = *(char **)arg3;
+		break;
+	case event_im_displayed_rcvd:
+		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
+		buf[0] = tmpbuf1;
+		buf[1] = arg2;
+		buf[2] = arg3 ? arg3 : tmpbuf3;
+		break;
+	case event_draw_menu:
+		/* we can't handle this usefully without gtk/perl bindings */
+		return 0;
+	default:
+		debug_printf("someone forgot to handle %s in the perl binding\n",
+					 gaim_event_get_name(event));
+		return 0;
+	}
+
+	/* Call any applicable functions */
+	for (handler = perl_event_handlers;
+		 handler != NULL;
+		 handler = handler->next) {
+
+		data = handler->data;
+
+		if (!strcmp(gaim_event_get_name(event), data->event_type)) {
+
+			handler_return = execute_perl(data->handler_name, 5, buf);
+
+			if (handler_return)
+				return handler_return;
+		}
+	}
+
+	/* Now make changes from perl scripts affect the real data */
+	switch (event) {
+	case event_im_recv:
+		if (buf[1] != *(char **)arg2) {
+			free(*(char **)arg2);
+			*(char **)arg2 = buf[1];
+		}
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_im_send:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_chat_recv:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		if (buf[3] != *(char **)arg4) {
+			free(*(char **)arg4);
+			*(char **)arg4 = buf[3];
+		}
+		break;
+	case event_chat_send_invite:
+		if (buf[3] != *(char **)arg4) {
+			free(*(char **)arg4);
+			*(char **)arg4 = buf[3];
+		}
+		break;
+	case event_chat_send:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	case event_im_displayed_sent:
+		if (buf[2] != *(char **)arg3) {
+			free(*(char **)arg3);
+			*(char **)arg3 = buf[2];
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+XS (XS_GAIM_add_event_handler)
+{
+	unsigned int junk;
+	struct _perl_event_handlers *handler;
+	char *handle;
+	GaimPlugin *plug;
+	GList *p;
+	dXSARGS;
+	items = 0;
+	
+	handle = SvPV(ST(0), junk);
+
+	for (p = gaim_plugins_get_all(); p != NULL; p = p->next) {
+		plug = p->data;
+
+		if (!strcmp(handle, plug->path))
+			break;
+	}
+
+	if (p) {
+		handler = g_new0(struct _perl_event_handlers, 1);
+		handler->event_type = g_strdup(SvPV(ST(1), junk));
+		handler->handler_name = g_strdup(SvPV(ST(2), junk));
+		handler->plug = plug;
+		perl_event_handlers = g_list_append(perl_event_handlers, handler);
+		debug_printf("registered perl event handler for %s\n", handler->event_type);
+	} else {
+		debug_printf("Invalid handle (%s) registering perl event handler\n", handle);
+	}
+	
+	XSRETURN_EMPTY;
+}
+
+XS (XS_GAIM_remove_event_handler)
+{
+	unsigned int junk;
+	struct _perl_event_handlers *ehn;
+	GList *cur = perl_event_handlers;
+	dXSARGS;
+	items = 0;
+
+	while (cur) {
+		GList *next = cur->next;
+		ehn = cur->data;
+
+		if (!strcmp(ehn->event_type, SvPV(ST(0), junk)) &&
+			!strcmp(ehn->handler_name, SvPV(ST(1), junk)))
+		{
+			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
+			g_free(ehn->event_type);
+			g_free(ehn->handler_name);
+			g_free(ehn);
+		}
+
+		cur = next;
+	}
+}
+
+static int
+perl_timeout(gpointer data)
+{
+	char *atmp[2] = { NULL, NULL };
+	struct _perl_timeout_handlers *handler = data;
+
+	atmp[0] = escape_quotes(handler->handler_args);
+	execute_perl(handler->handler_name, 1, atmp);
+
+	perl_timeout_handlers = g_list_remove(perl_timeout_handlers, handler);
+	g_free(handler->handler_args);
+	g_free(handler->handler_name);
+	g_free(handler);
+
+	return 0; /* returning zero removes the timeout handler */
+}
+
+XS (XS_GAIM_add_timeout_handler)
+{
+	unsigned int junk;
+	long timeout;
+	struct _perl_timeout_handlers *handler;
+	char *handle;
+	GaimPlugin *plug;
+	GList *p;
+	
+	dXSARGS;
+	items = 0;
+	
+	handle = SvPV(ST(0), junk);
+
+	for (p = gaim_plugins_get_all(); p != NULL; p = p->next) {
+		plug = p->data;
+
+		if (!strcmp(handle, plug->path))
+			break;
+	}
+
+	if (p) {
+		handler = g_new0(struct _perl_timeout_handlers, 1);
+		timeout = 1000 * SvIV(ST(1));
+		debug_printf("Adding timeout for %ld seconds.\n", timeout/1000);
+		handler->plug = plug;
+		handler->handler_name = g_strdup(SvPV(ST(2), junk));
+		handler->handler_args = g_strdup(SvPV(ST(3), junk));
+		perl_timeout_handlers = g_list_append(perl_timeout_handlers, handler);
+		handler->iotag = g_timeout_add(timeout, perl_timeout, handler);
+	} else {
+		debug_printf("Invalid handle (%s) in adding perl timeout handler.", handle);
+	}
+	XSRETURN_EMPTY;
+}
+
+XS (XS_GAIM_play_sound)
+{
+	int id;
+	dXSARGS;
+
+	items = 0;
+
+	id = SvIV(ST(0));
+
+	gaim_sound_play_event(id);
+
+	XSRETURN_EMPTY;
+}
+
+static gboolean
+probe_perl_plugin(GaimPlugin *plugin)
+{
+	/* XXX This would be much faster if I didn't create a new
+	 *     PerlInterpreter every time I probed a plugin */
+
+	GaimPluginInfo *info;
+	PerlInterpreter *prober = perl_alloc();
+	char *argv[] = {"", plugin->path };
+	int count;
+	gboolean status = TRUE;
+
+	perl_construct(prober);
+	perl_parse(prober, NULL, 2, argv, NULL);
+	
+	{
+		dSP;
+		ENTER;
+		SAVETMPS;
+		PUSHMARK(SP);
+
+		count = perl_call_pv("description", G_NOARGS | G_ARRAY | G_EVAL);
+		SPAGAIN;
+
+		if (count == 6) {
+			info = g_new0(GaimPluginInfo, 1);
+
+			info->api_version  = 2;
+			info->type         = GAIM_PLUGIN_STANDARD;
+
+			info->dependencies = g_list_append(info->dependencies,
+											   PERL_PLUGIN_ID);
+
+			POPp; /* iconfile */
+
+			info->homepage    = g_strdup(POPp);
+			info->author      = g_strdup(POPp);
+			info->description = g_strdup(POPp);
+			info->version     = g_strdup(POPp);
+			info->name        = g_strdup(POPp);
+
+			plugin->info = info;
+
+			if (!gaim_plugin_register(plugin))
+				status = FALSE;
+		}
+		else
+			status = FALSE;
+
+		PUTBACK;
+		FREETMPS;
+		LEAVE;
+	}
+
+	perl_destruct(prober);
+	perl_free(prober);
+
+	return status;
+}
+
+static gboolean
+load_perl_plugin(GaimPlugin *plugin)
+{
+	perl_load_file(plugin->path, plugin);
+
+	return TRUE;
+}
+
+static gboolean
+unload_perl_plugin(GaimPlugin *plugin)
+{
+	perl_unload_file(plugin);
+
+	return TRUE;
+}
+
+static void
+destroy_perl_plugin(GaimPlugin *plugin)
+{
+	if (plugin->info != NULL) {
+		g_free(plugin->info->name);
+		g_free(plugin->info->version);
+		g_free(plugin->info->description);
+		g_free(plugin->info->author);
+		g_free(plugin->info->homepage);
+	}
+}
+
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+	perl_end();
+
+	return TRUE;
+}
+
+static GaimPluginLoaderInfo loader_info =
+{
+	NULL,                                             /**< exts           */
+
+	probe_perl_plugin,                                /**< probe          */
+	load_perl_plugin,                                 /**< load           */
+	unload_perl_plugin,                               /**< unload         */
+	destroy_perl_plugin,                              /**< destroy        */
+	perl_event                                        /**< broadcast      */
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_LOADER,                               /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	PERL_PLUGIN_ID,                                   /**< id             */
+	N_("Perl Plugin Loader"),                         /**< name           */
+	VERSION,                                          /**< version        */
+	N_("Provides support for loading perl plugins."), /**< summary        */
+	N_("Provides support for loading perl plugins."), /**< description    */
+	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&loader_info                                      /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+	loader_info.exts = g_list_append(loader_info.exts, "pl");
+}
+
+GAIM_INIT_PLUGIN(perl, __init_plugin, info);
--- a/plugins/raw.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/raw.c	Fri Apr 25 06:47:33 2003 +0000
@@ -19,7 +19,7 @@
 
 struct gaim_plugin_description desc; 
 struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
+	desc.api_version = GAIM_PLUGIN_API_VERSION;
 	desc.name = g_strdup("Raw Input");
 	desc.version = g_strdup(VERSION);
 	desc.description = g_strdup("Lets you send raw input to text-vased protocols (Jabber, MSN, IRC, TOC).  Hit 'Enter' in the entry box to send.  Watch the debug window.");
@@ -43,7 +43,7 @@
 
 static int goodbye()
 {
-	gaim_plugin_unload(me);
+	gaim_plugin_unload_self(me);
 	return FALSE;
 }
 
--- a/plugins/simple.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/simple.c	Fri Apr 25 06:47:33 2003 +0000
@@ -1,36 +1,52 @@
-#define GAIM_PLUGINS
-
-#include <stdio.h>
+#include "config.h"
 #include "gaim.h"
 
-static GModule *handle = NULL;
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	debug_printf("simple plugin loaded.\n");
 
-char *gaim_plugin_init(GModule *h) {
-	printf("plugin loaded.\n");
-	handle = h;
-	return NULL;
+	return TRUE;
 }
 
-void gaim_plugin_remove() {
-	printf("plugin unloaded.\n");
-	handle = NULL;
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+	debug_printf("simple plugin unloaded.\n");
+
+	return TRUE;
 }
 
-struct gaim_plugin_description desc; 
-struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup("Simple Plugin");
-	desc.version = g_strdup("1.0");
-	desc.description = g_strdup("Tests to see that most things are working.");
-	desc.authors = g_strdup("Eric Warmehoven &lt;eric@warmenhoven.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	NULL,                                             /**< id             */
+	N_("Simple Plugin"),                              /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Tests to see that most things are working."),
+	                                                  /**  description    */
+	N_("Tests to see that most things are working."),
+	"Eric Warmenhoven <eric@warmenhoven.org>",        /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
 
-char *name() {
-	return "Simple Plugin Version 1.0";
-}
-
-char *description() {
-	return "Tests to see that most things are working.";
-}
+GAIM_INIT_PLUGIN(simple, __init_plugin, info);
--- a/plugins/spellchk.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/spellchk.c	Fri Apr 25 06:47:33 2003 +0000
@@ -3,18 +3,15 @@
  * or nearly directly from xchat, version 1.4.2 by Peter Zelezny and others.
  *
  * TODO:
- * 	? I think i did everything i want to with it.
+ *	? I think i did everything i want to with it.
  *
  * BUGS:
- * 	? I think i fixed them all.
+ *	? I think i fixed them all.
  */
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include "gaim.h"
+#include "gtkplugin.h"
 
 #include <string.h>
 #include <ctype.h>
@@ -27,6 +24,8 @@
 #include "win32dep.h"
 #endif
 
+#define SPELLCHECK_PLUGIN_ID "gtk-spellcheck"
+
 enum {
 	BAD_COLUMN,
 	GOOD_COLUMN,
@@ -382,37 +381,19 @@
  *  EXPORTED FUNCTIONS
  */
 
-G_MODULE_EXPORT char *gaim_plugin_init(GModule *handle) {
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
 	load_conf();
 
-	gaim_signal_connect(handle, event_im_send, substitute_words, NULL);
-	gaim_signal_connect(handle, event_chat_send, substitute_words, NULL);
-	return NULL;
-}
+	gaim_signal_connect(plugin, event_im_send, substitute_words, NULL);
+	gaim_signal_connect(plugin, event_chat_send, substitute_words, NULL);
 
-G_MODULE_EXPORT void gaim_plugin_remove() {
+	return TRUE;
 }
 
-struct gaim_plugin_description desc; 
-G_MODULE_EXPORT struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Text replacement"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Replaces text in outgoing messages according to user-defined rules."));
-	desc.authors = g_strdup("Eric Warmehoven &lt;eric@warmenhoven.org>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
-}
- 
-G_MODULE_EXPORT char *name() {
-	return _("Text replacement");
-}
-
-G_MODULE_EXPORT char *description() {
-	return _("Replaces text in outgoing messages according to user-defined rules.");
-}
-
-G_MODULE_EXPORT GtkWidget *gaim_plugin_config_gtk()
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
 {
 	GtkWidget *ret, *vbox, *win;
 	GtkWidget *hbox, *label;
@@ -534,3 +515,37 @@
 	gtk_widget_show_all(ret);
 	return ret;
 }
+
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame
+};
+
+static GaimPluginInfo info =
+{
+	2,
+	GAIM_PLUGIN_STANDARD,
+	GAIM_GTK_PLUGIN_TYPE,
+	0,
+	NULL,
+	GAIM_PRIORITY_DEFAULT,
+	SPELLCHECK_PLUGIN_ID,
+	N_("Text replacement"),
+	VERSION,
+	N_("Replaces text in outgoing messages according to user-defined rules."),
+	N_("Replaces text in outgoing messages according to user-defined rules."),
+	"Eric Warmnenhoven <eric@warmenhoven.org>",
+	WEBSITE,
+	plugin_load,
+	NULL,
+	NULL,
+	&ui_info,
+	NULL
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+}
+
+GAIM_INIT_PLUGIN(spellcheck, __init_plugin, info);
--- a/plugins/ticker/ticker.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/ticker/ticker.c	Fri Apr 25 06:47:33 2003 +0000
@@ -28,19 +28,18 @@
 #include <string.h>
 #include <stdlib.h>
 #include "gaim.h"
+#include "prpl.h"
+#include "gtkplugin.h"
 #include "list.h"
 #include "gtklist.h"
 #ifdef _WIN32
 #include "win32dep.h"
 #endif
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
+#define TICKER_PLUGIN_ID "gtk-ticker"
 
 static GtkWidget *tickerwindow = NULL;
 static GtkWidget *ticker;
-static GModule *handle;
 
 typedef struct {
 	struct buddy *buddy;
@@ -236,43 +235,69 @@
  *  EXPORTED FUNCTIONS
  */
 
-G_MODULE_EXPORT char *name() {
-	return _("Buddy Ticker");
-}
-
-G_MODULE_EXPORT char *description() {
-	return _("A horizontal scrolling version of the buddy list.");
-}
-
-G_MODULE_EXPORT char *gaim_plugin_init(GModule *h) {
-	handle = h;
-
-	gaim_signal_connect(h, event_buddy_signon, signon_cb, NULL);
-	gaim_signal_connect(h, event_signoff, signoff_cb, NULL);
-	gaim_signal_connect(h, event_buddy_signoff, buddy_signoff_cb, NULL);
-	gaim_signal_connect(h, event_buddy_away, away_cb, NULL);
-	gaim_signal_connect(h, event_buddy_back, away_cb, NULL);
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	gaim_signal_connect(plugin, event_buddy_signon, signon_cb, NULL);
+	gaim_signal_connect(plugin, event_signoff, signoff_cb, NULL);
+	gaim_signal_connect(plugin, event_buddy_signoff, buddy_signoff_cb, NULL);
+	gaim_signal_connect(plugin, event_buddy_away, away_cb, NULL);
+	gaim_signal_connect(plugin, event_buddy_back, away_cb, NULL);
 
 	if (connections)
 		buddy_ticker_show();
-	return NULL;
+
+	return TRUE;
 }
 
-G_MODULE_EXPORT void gaim_plugin_remove() {
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
 	while(tickerbuds) {
 		g_free(tickerbuds->data);
 		tickerbuds = g_list_delete_link(tickerbuds, tickerbuds);
 	}
+
 	gtk_widget_destroy(tickerwindow);
+
+	return TRUE;
 }
 
-struct gaim_plugin_description desc;
-G_MODULE_EXPORT struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Buddy Ticker"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("A horizontal scrolling version of the buddy list."));
-	desc.authors = g_strdup("Syd Logan");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
+static GaimGtkPluginUiInfo ui_info =
+{
+	NULL                                            /**< get_config_frame */
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	TICKER_PLUGIN_ID,                                 /**< id             */
+	N_("Buddy Ticker"),                               /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("A horizontal scrolling version of the buddy list."),
+	                                                  /**  description    */
+	N_("A horizontal scrolling version of the buddy list."),
+	"Syd Logan",                                      /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	&ui_info,                                         /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
+
+GAIM_INIT_PLUGIN(ticker, __init_plugin, info);
--- a/plugins/timestamp.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/plugins/timestamp.c	Fri Apr 25 06:47:33 2003 +0000
@@ -5,19 +5,17 @@
 
 #include "config.h"
 
-#ifndef GAIM_PLUGINS
-#define GAIM_PLUGINS
-#endif
-
 #include <time.h>
 #include "gaim.h"
 #include "gtkimhtml.h"
+#include "gtkplugin.h"
+
+#define TIMESTAMP_PLUGIN_ID "gtk-timestamp"
 
 //Set the default to 5 minutes.
-int timestamp = 5 * 60 * 1000;
+static int timestamp = 5 * 60 * 1000;
 
-GModule *handle;
-GSList *timestamp_timeouts;
+static GSList *timestamp_timeouts;
 
 gboolean do_timestamp (gpointer data)
 {
@@ -59,7 +57,9 @@
 	timestamp = tm;
 }
 
-GtkWidget *gaim_plugin_config_gtk() {
+static GtkWidget *
+get_config_frame(GaimPlugin *plugin)
+{
 	GtkWidget *ret;
 	GtkWidget *frame, *label;
 	GtkWidget *vbox, *hbox;
@@ -97,39 +97,71 @@
 	return ret;
 }
 
-
-char *gaim_plugin_init(GModule *h) {
-	GList *cnvs = gaim_get_conversations();
+static gboolean
+plugin_load(GaimPlugin *plugin)
+{
+	GList *cnvs;
 	struct gaim_conversation *c;
-	handle = h;
 
-	while (cnvs) {
+	for (cnvs = gaim_get_conversations(); cnvs != NULL; cnvs = cnvs->next) {
 		c = cnvs->data;
 		timestamp_new_convo(c->name);
-		cnvs = cnvs->next;
 	}
-	gaim_signal_connect(handle, event_new_conversation, timestamp_new_convo, NULL);
+
+	gaim_signal_connect(plugin, event_new_conversation,
+						timestamp_new_convo, NULL);
+
+	return TRUE;
+}
 
-	return NULL;
+static gboolean
+plugin_unload(GaimPlugin *plugin)
+{
+	GSList *to;
+
+	for (to = timestamp_timeouts; to != NULL; to = to->next)
+		g_source_remove(GPOINTER_TO_INT(to->data));
+
+	g_slist_free(timestamp_timeouts);
+
+	return TRUE;
 }
 
-void gaim_plugin_remove() {
-	GSList *to;
-	to = timestamp_timeouts;
-	while (to) {
-		g_source_remove(GPOINTER_TO_INT(to->data));
-		to = to->next;
-	}
-	g_slist_free(timestamp_timeouts);
+static GaimGtkPluginUiInfo ui_info =
+{
+	get_config_frame
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_STANDARD,                             /**< type           */
+	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	TIMESTAMP_PLUGIN_ID,                              /**< id             */
+	N_("Timestamp"),                                  /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Adds iChat-style timestamps to conversations every N minutes."),
+	                                                  /**  description    */
+	N_("Adds iChat-style timestamps to conversations every N minutes."),
+	"Sean Egan <bj91704@binghamton.edu>",             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	plugin_load,                                      /**< load           */
+	plugin_unload,                                    /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	&ui_info,                                         /**< ui_info        */
+	NULL                                              /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 }
 
-struct gaim_plugin_description desc; 
-struct gaim_plugin_description *gaim_plugin_desc() {
-	desc.api_version = PLUGIN_API_VERSION;
-	desc.name = g_strdup(_("Timestamp"));
-	desc.version = g_strdup(VERSION);
-	desc.description = g_strdup(_("Adds iChat-style timestamps to conversations every N minutes."));
-	desc.authors = g_strdup("Sean Egan &lt;bj91704@binghamton.edu>");
-	desc.url = g_strdup(WEBSITE);
-	return &desc;
-}
+GAIM_INIT_PLUGIN(timestamp, __init_plugin, info);
--- a/src/Makefile.am	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/Makefile.am	Fri Apr 25 06:47:33 2003 +0000
@@ -18,6 +18,8 @@
 		dialogs.c \
 		dnd-hints.c \
 		dnd-hints.h \
+		event.c \
+		event.h \
 		ft.c \
 		ft.h \
 		gaim.h \
@@ -34,6 +36,8 @@
 		gtkimhtml.c \
 		gtkimhtml.h \
 		gtklist.h \
+		gtkplugin.c \
+		gtkplugin.h \
 		gtkpounce.c \
 		gtkpounce.h \
 		gtkutils.c \
@@ -46,10 +50,10 @@
 		main.c \
 		md5.c \
 		md5.h \
-		module.c \
+		plugin.c \
+		plugin.h \
 		multi.c \
 		multi.h \
-		perl.c \
 		pounce.c \
 		pounce.h \
 		prefs.c \
@@ -77,7 +81,6 @@
 	$(GTK_LIBS) \
 	$(SOUND_LIBS) \
 	$(STATIC_LINK_LIBS) \
-	$(PERL_LIBS) \
 	$(XSS_LIBS) \
 	$(SM_LIBS) \
 	$(INTLLIBS) \
@@ -88,7 +91,6 @@
 gaim_remote_LDADD = @LIBOBJS@ $(GLIB_LIBS)
 
 AM_CPPFLAGS = \
-	$(PERL_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
 	$(AUDIOFILE_CFLAGS) \
 	$(AO_CFLAGS) \
--- a/src/away.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/away.c	Fri Apr 25 06:47:33 2003 +0000
@@ -32,6 +32,7 @@
 #include "prpl.h"
 #include "gtkimhtml.h"
 #include "gtklist.h"
+#include "plugin.h"
 
 GtkWidget *imaway = NULL;
 
@@ -349,6 +350,8 @@
 	struct away_message *a;
 	GSList *con = connections;
 	struct gaim_connection *gc = NULL;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
 	int count = 0;
 
 	if (prefs_away_store != NULL) {
@@ -406,7 +409,10 @@
 
 		while (con) {
 			gc = con->data;
-			if (gc->prpl->away_states &&gc->prpl->set_away)
+
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+			if (prpl_info->away_states != NULL && prpl_info->set_away != NULL)
 				count++;
 			con = g_slist_next(con);
 		}
@@ -417,12 +423,15 @@
 			GList *msgs, *tmp;
 			while (con) {
 				gc = con->data;
-				if (gc->prpl->away_states &&gc->prpl->set_away)
+
+				prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+				if (prpl_info->away_states && prpl_info->set_away)
 					break;
 				con = g_slist_next(con);
 			}
 
-			tmp = msgs = gc->prpl->away_states(gc);
+			tmp = msgs = prpl_info->away_states(gc);
 
 			if ((g_list_length(msgs) == 1) && !strcmp(msgs->data, GAIM_AWAY_CUSTOM)) {
 				awy = away_messages;
@@ -484,13 +493,15 @@
 				GList *msgs, *tmp;
 				gc = con->data;
 
-				if (!gc->prpl->away_states ||!gc->prpl->set_away) {
+				prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+				if (!prpl_info->away_states || !prpl_info->set_away) {
 					con = con->next;
 					continue;
 				}
 
 				g_snprintf(buf, sizeof(buf), "%s (%s)",
-					   gc->username, gc->prpl->name);
+					   gc->username, gc->prpl->info->name);
 				menuitem = gtk_image_menu_item_new_with_label(buf);
 
 				pixbuf = create_prpl_icon(gc->account);
@@ -511,7 +522,7 @@
 				gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
 				gtk_widget_show(submenu);
 
-				tmp = msgs = gc->prpl->away_states(gc);
+				tmp = msgs = prpl_info->away_states(gc);
 
 				if ((g_list_length(msgs) == 1) &&
 				    (!strcmp(msgs->data, GAIM_AWAY_CUSTOM))) {
--- a/src/buddy.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/buddy.c	Fri Apr 25 06:47:33 2003 +0000
@@ -315,7 +315,8 @@
 	GtkWidget *menu, *menuitem;
 	GtkTreeSelection *sel;
 	GList *list;
-	struct prpl *prpl;
+	GaimPlugin *prpl = NULL;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 
 	if (event->button != 3)
 		return FALSE;
@@ -334,9 +335,12 @@
 		gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL);
 	} else if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
 		/* Protocol specific options */
-		prpl = find_prpl(((struct buddy*)node)->account->protocol);
+		prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol);
 
-		if (prpl && prpl->get_info)
+		if (prpl != NULL)
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if (prpl && prpl_info->get_info)
 			gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL);
 
 		gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL);
@@ -344,7 +348,7 @@
 		gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL);
 
 		if (prpl) {
-			list = prpl->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
+			list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name);
 			while (list) {
 				struct proto_buddy_menu *pbm = list->data;
 				menuitem = gtk_menu_item_new_with_mnemonic(pbm->label);
@@ -355,7 +359,7 @@
 			}
 		}
 
-		plugin_event (event_draw_menu, menu, ((struct buddy *) node)->name);
+		gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name);
 
 		gaim_separator(menu);
 		gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL);
@@ -676,15 +680,19 @@
 
 static char *gaim_get_tooltip_text(struct buddy *b)
 {
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	char *text = NULL;
-	struct prpl* prpl = find_prpl(b->account->protocol);
 	char *statustext = NULL;
 	char *aliastext = NULL, *nicktext = NULL;
 	char *warning = NULL, *idletime = NULL;
 
-	if (prpl->tooltip_text) {
+	prpl = gaim_find_prpl(b->account->protocol);
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->tooltip_text) {
 		const char *end;
-		statustext = prpl->tooltip_text(b);
+		statustext = prpl_info->tooltip_text(b);
 
 		if(statustext && !g_utf8_validate(statustext, -1, &end)) {
 			char *new = g_strndup(statustext,
@@ -760,15 +768,20 @@
 
 	int scalesize = 30;
 
-	struct prpl* prpl = find_prpl(b->account->protocol);
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	prpl = gaim_find_prpl(b->account->protocol);
 
 	if (!prpl) 
 		return NULL;
 
-	if (prpl->list_icon)
-		protoname = prpl->list_icon(b->account, b);
-	if (b->present != GAIM_BUDDY_SIGNING_OFF && prpl->list_emblems)
-		prpl->list_emblems(b, &se, &sw, &nw, &ne);
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->list_icon)
+		protoname = prpl_info->list_icon(b->account, b);
+	if (b->present != GAIM_BUDDY_SIGNING_OFF && prpl_info->list_emblems)
+		prpl_info->list_emblems(b, &se, &sw, &nw, &ne);
 
 	if (size == GAIM_STATUS_ICON_SMALL) {
 		scalesize = 15;
@@ -929,14 +942,19 @@
 {
 	char *name = gaim_get_buddy_alias(b);
 	char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL;
-	struct prpl* prpl = find_prpl(b->account->protocol);
-
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	/* XXX Clean up this crap */
 
 	int ihrs, imin;
 	char *idletime = NULL, *warning = NULL, *statustext = NULL;
 	time_t t;
 
+	prpl = gaim_find_prpl(b->account->protocol);
+
+	if (prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
 	if (!(blist_options & OPT_BLIST_SHOW_ICONS)) {
 		if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) {
 			text =  g_strdup_printf("<span color='dim grey'>%s</span>",
@@ -952,8 +970,8 @@
 	ihrs = (t - b->idle) / 3600;
 	imin = ((t - b->idle) / 60) % 60;
 
-	if (prpl && prpl->status_text) {
-		char *tmp = prpl->status_text(b);
+	if (prpl && prpl_info->status_text) {
+		char *tmp = prpl_info->status_text(b);
 		const char *end;
 
 		if(tmp && !g_utf8_validate(tmp, -1, &end)) {
@@ -1755,61 +1773,36 @@
 GdkPixbuf *
 create_prpl_icon(struct gaim_account *account)
 {
-	struct prpl *prpl = find_prpl(account->protocol);
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	GdkPixbuf *status = NULL;
 	char *filename = NULL;
 	const char *protoname = NULL;
+	char buf[256];
 
-	/* this is so we can get the icon when the prpl isn't loaded.
-	 * it's not as bad as it looks, since most of the time this function
-	 * is called, the prpl is already loaded, so it'll just increment and
-	 * decrement the refcount, won't have to go through the hassle of
-	 * actually loading and unloading the prpl.  the few times it actually
-	 * does that, it saves us from crashing */
-	if(prpl && prpl->list_icon) {
-		ref_protocol(prpl);
-		protoname = prpl->list_icon(account, NULL);
-		unref_protocol(prpl);
+	prpl = gaim_find_prpl(account->protocol);
+
+	if (prpl != NULL) {
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if (prpl_info->list_icon != NULL)
+			protoname = prpl_info->list_icon(account, NULL);
 	}
 
-	if (!protoname)
+	if (protoname == NULL)
 		return NULL;
 
-	/* "Hey, what's all this crap?" you ask.  Status icons will be themeable too, and 
-	   then it will look up protoname from the theme */
-	if (!strcmp(protoname, "aim")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "aim.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "yahoo")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "yahoo.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "msn")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "msn.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "jabber")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "jabber.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "icq")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "icq.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "gadu-gadu")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "gadu-gadu.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "napster")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "napster.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	} else if (!strcmp(protoname, "irc")) {
-		filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "irc.png", NULL);
-		status = gdk_pixbuf_new_from_file(filename,NULL);
-		g_free(filename);
-	}
+	/*
+	 * Status icons will be themeable too, and then it will look up
+	 * protoname from the theme
+	 */
+	g_snprintf(buf, sizeof(buf), "%s.png", protoname);
+
+	filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status",
+								"default", buf, NULL);
+	status = gdk_pixbuf_new_from_file(filename, NULL);
+	g_free(filename);
+
 	return status;
 }
 
--- a/src/buddy_chat.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/buddy_chat.c	Fri Apr 25 06:47:33 2003 +0000
@@ -103,7 +103,7 @@
 
 	chatentries = NULL;
 
-	list = joinchatgc->prpl->chat_info(joinchatgc);
+	list = GAIM_PLUGIN_PROTOCOL_INFO(joinchatgc->prpl)->chat_info(joinchatgc);
 
 	for (tmp = list; tmp != NULL; tmp = tmp->next) {
 		GtkWidget *label;
@@ -189,13 +189,14 @@
 	for (c = connections; c != NULL; c = c->next) {
 		g = (struct gaim_connection *)c->data;
 
-		if (!g->prpl->join_chat)
+		if (!GAIM_PLUGIN_PROTOCOL_INFO(g->prpl)->join_chat)
 			continue;
 
 		if (!joinchatgc)
 			joinchatgc = g;
 
-		g_snprintf(buf, sizeof(buf), "%s (%s)", g->username, g->prpl->name);
+		g_snprintf(buf, sizeof(buf), "%s (%s)",
+				   g->username, g->prpl->info->name);
 		opt = gtk_menu_item_new_with_label(buf);
 
 		g_object_set_data(G_OBJECT(opt), "gaim_connection", g);
@@ -238,7 +239,7 @@
 	for (c = connections; c != NULL; c = c->next) {
 		gc = c->data;
 
-		if (gc->prpl->join_chat)
+		if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->join_chat)
 			break;
 
 		gc = NULL;
--- a/src/conversation.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/conversation.c	Fri Apr 25 06:47:33 2003 +0000
@@ -217,7 +217,7 @@
 	else
 		buffy = g_strdup(buf);
 
-	plugin_return = plugin_event(
+	plugin_return = gaim_event_broadcast(
 			(type == GAIM_CONV_IM ? event_im_send : event_chat_send),
 			gc,
 			(type == GAIM_CONV_IM
@@ -245,7 +245,7 @@
 		struct gaim_im *im = GAIM_IM(conv);
 
 		buffy = g_strdup(buf);
-		plugin_event(event_im_displayed_sent, gc,
+		gaim_event_broadcast(event_im_displayed_sent, gc,
 					 gaim_conversation_get_name(conv), &buffy);
 
 		if (buffy != NULL) {
@@ -918,7 +918,7 @@
 		place_conv(conv);
 	}
 
-	plugin_event(event_new_conversation, name);
+	gaim_event_broadcast(event_new_conversation, name);
 
 	return conv;
 }
@@ -926,6 +926,7 @@
 void
 gaim_conversation_destroy(struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_window *win;
 	struct gaim_conversation_ui_ops *ops;
 	struct gaim_connection *gc;
@@ -940,12 +941,14 @@
 	gc   = gaim_conversation_get_gc(conv);
 	name = gaim_conversation_get_name(conv);
 
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 	if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
 		if (!(misc_options & OPT_MISC_STEALTH_TYPING))
 			serv_send_typing(gc, (char *)name, NOT_TYPING);
 
-		if (gc && gc->prpl->convo_closed != NULL)
-			gc->prpl->convo_closed(gc, (char *)name);
+		if (gc && prpl_info->convo_closed != NULL)
+			prpl_info->convo_closed(gc, (char *)name);
 	}
 	else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
 		/*
@@ -972,7 +975,7 @@
 		}
 	}
 
-	plugin_event(event_del_conversation, conv);
+	gaim_event_broadcast(event_del_conversation, conv);
 
 	if (conv->name  != NULL) g_free(conv->name);
 	if (conv->title != NULL) g_free(conv->title);
@@ -1403,6 +1406,7 @@
 						const char *message, size_t length, int flags,
 						time_t mtime)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_account *account;
 	struct gaim_conversation_ui_ops *ops;
 	struct gaim_window *win;
@@ -1428,8 +1432,10 @@
 		!g_list_find(gaim_get_conversations(), conv))
 		return;
 
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(account->gc->prpl);
+
 	if (gaim_conversation_get_type(conv) == GAIM_CONV_IM ||
-		!(account->gc->prpl->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+		!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
 
 		if (who == NULL) {
 			if (flags & WFLAG_SEND) {
@@ -1939,7 +1945,7 @@
 		g_list_insert_sorted(gaim_chat_get_users(chat), g_strdup(user),
 							 insertname_compare));
 
-	plugin_event(event_chat_buddy_join,
+	gaim_event_broadcast(event_chat_buddy_join,
 				 gaim_conversation_get_gc(conv), gaim_chat_get_id(chat),
 				 user);
 
@@ -2021,7 +2027,7 @@
 	conv = gaim_chat_get_conversation(chat);
 	ops  = gaim_conversation_get_ui_ops(conv);
 
-	plugin_event(event_chat_buddy_leave, gaim_conversation_get_gc(conv),
+	gaim_event_broadcast(event_chat_buddy_leave, gaim_conversation_get_gc(conv),
 				 gaim_chat_get_id(chat), user);
 
 	if (ops != NULL && ops->chat_remove_user != NULL)
--- a/src/core.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/core.c	Fri Apr 25 06:47:33 2003 +0000
@@ -190,7 +190,7 @@
 {
 #ifdef GAIM_PLUGINS
 	guint id;
-	struct gaim_plugin *p;
+	GaimPlugin *p;
 
 	switch (subtype) {
 		/*
@@ -198,15 +198,13 @@
 		break;
 		*/
 	case CUI_PLUGIN_LOAD:
-		p = load_plugin(data);
-		/* XXX need to broadcast to UIs that plugin has been loaded */
+		gaim_plugin_load(gaim_plugin_probe(data));
 		break;
 	case CUI_PLUGIN_UNLOAD:
 		memcpy(&id, data, sizeof(id));
-		p = g_list_nth_data(plugins, id);
+		p = g_list_nth_data(gaim_plugins_get_loaded(), id);
 		if (p) {
-			unload_plugin(p);
-			/* XXX need to broadcast to UIs that plugin has been unloaded */
+			gaim_plugin_unload(p);
 		}
 		break;
 	default:
--- a/src/core.h	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/core.h	Fri Apr 25 06:47:33 2003 +0000
@@ -53,6 +53,8 @@
 #include "conversation.h"
 #include "ft.h"
 #include "privacy.h"
+#include "plugin.h"
+#include "event.h"
 
 /* Really user states are controlled by the PRPLs now. We just use this for event_away */
 #define UC_UNAVAILABLE  1
@@ -84,91 +86,15 @@
 	int permdeny;
 };
 
-enum gaim_event {
-	event_signon = 0,
-	event_signoff,
-	event_away,
-	event_back,
-	event_im_recv,
-	event_im_send,
-	event_buddy_signon,
-	event_buddy_signoff,
-	event_buddy_away,
-	event_buddy_back,
-	event_buddy_idle,
-	event_buddy_unidle,
-	event_blist_update,
-	event_chat_invited,
-	event_chat_join,
-	event_chat_leave,
-	event_chat_buddy_join,
-	event_chat_buddy_leave,
-	event_chat_recv,
-	event_chat_send,
-	event_warned,
-	event_error,
-	event_quit,
-	event_new_conversation,
-	event_set_info,
-	event_draw_menu,
-	event_im_displayed_sent,
-	event_im_displayed_rcvd,
-	event_chat_send_invite,
-	event_got_typing,
-	event_del_conversation,
-	event_connecting,
-	/* any others? it's easy to add... */
-};
-
 struct UI {
 	GIOChannel *channel;
 	guint inpa;
 };
 
-#define USE_PLUGINS GAIM_PLUGINS || USE_PERL
-#define PLUGIN_API_VERSION 1
-enum gaim_plugin_type {
-	perl_script,
-	plugin
-};
-
-struct gaim_plugin_description {	
-	int api_version;
-	gchar *name;
-	gchar *version;
-	gchar *description;
-	gchar *authors;
-	gchar *url;
-	gchar *iconfile;
-};
-
-struct gaim_plugin {
-	enum gaim_plugin_type type;
-	void *handle;
-	gchar path[128];
-	struct gaim_plugin_description desc;
-	gchar error[128];
-	void *iter;
-};
-
-#ifdef GAIM_PLUGINS
-struct gaim_callback {
-	GModule *handle;
-	enum gaim_event event;
-	void *function;
-	void *data;
-};
-#endif
-
 /* Globals in core.c */
 extern GSList *uis;
 extern int gaim_session;
 
-/* Globals in plugins.c */
-extern GList *plugins;
-extern GList *probed_plugins;
-extern GList *callbacks;
-
 /* Functions in core.c */
 extern gint UI_write(struct UI *, guchar *, int);
 extern void UI_build_write(struct UI *, guchar, guchar, ...);
@@ -184,31 +110,6 @@
 extern void load_pounces();
 extern void save_prefs();
 
-/* Functions in perl.c */
-#ifdef USE_PERL
-extern void perl_end();
-extern int perl_event(enum gaim_event, void *, void *, void *, void *, void *);
-extern int perl_load_file(char *);
-extern void perl_unload_file(struct gaim_plugin *);
-extern void unload_perl_scripts();
-extern void list_perl_scripts();
-extern struct gaim_plugin *probe_perl(char *);
-#endif
-
-/* Functions in plugins.c */
-#ifdef GAIM_PLUGINS
-extern struct gaim_plugin *load_plugin(const 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 *);
-extern void remove_all_plugins();
-#endif
-extern void gaim_probe_plugins();
-extern int plugin_event(enum gaim_event, ...);
-extern char *event_name(enum gaim_event);
-
 /* Functions in server.c */
 extern void serv_got_update(struct gaim_connection *, char *, int, int, time_t, time_t, int);
 extern void serv_got_im(struct gaim_connection *, const char *, const char *, guint32, time_t, gint);
--- a/src/dialogs.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/dialogs.c	Fri Apr 25 06:47:33 2003 +0000
@@ -733,11 +733,13 @@
 
 			while (g) {
 				c = (struct gaim_connection *)g->data;
-				if (!c->prpl->send_im) {
+
+				if (!GAIM_PLUGIN_PROTOCOL_INFO(c->prpl)->send_im) {
 					g = g->next;
 					continue;
 				}
-				g_snprintf(buf, sizeof(buf), "%s (%s)", c->username, c->prpl->name);
+				g_snprintf(buf, sizeof(buf), "%s (%s)",
+						   c->username, c->prpl->info->name);
 				opt = gtk_menu_item_new_with_label(buf);
 				g_object_set_data(G_OBJECT(opt), "getuserinfo", info);
 
@@ -833,11 +835,13 @@
 
 		while (g) {
 			c = (struct gaim_connection *)g->data;
-			if (!c->prpl->get_info) {
+
+			if (!GAIM_PLUGIN_PROTOCOL_INFO(c->prpl)->get_info) {
 				g = g->next;
 				continue;
 			}
-			g_snprintf(buf, sizeof(buf), "%s (%s)", c->username, c->prpl->name);
+			g_snprintf(buf, sizeof(buf), "%s (%s)",
+					   c->username, c->prpl->info->name);
 			opt = gtk_menu_item_new_with_label(buf);
 			g_object_set_data(G_OBJECT(opt), "getuserinfo", info);
 
@@ -1033,7 +1037,7 @@
 	while (g) {
 		c = (struct gaim_connection *)g->data;
 		g_snprintf(buf, sizeof(buf), "%s (%s)", 
-				c->username, c->prpl->name);
+				c->username, c->prpl->info->name);
 		opt = gtk_menu_item_new_with_label(buf);
 		g_object_set_data(G_OBJECT(opt), "addbuddy", b);
 		g_signal_connect(GTK_OBJECT(opt), "activate",
@@ -1328,9 +1332,12 @@
 	while (c) {
 		gc = (struct gaim_connection *)c->data;
 		c = c->next;
-		if (!gc->prpl->set_permit_deny)
+
+		if (!GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->set_permit_deny)
 			continue;
-		g_snprintf(buf, sizeof buf, "%s (%s)", gc->username, gc->prpl->name);
+
+		g_snprintf(buf, sizeof buf, "%s (%s)",
+				   gc->username, gc->prpl->info->name);
 		opt = gtk_menu_item_new_with_label(buf);
 		g_signal_connect(GTK_OBJECT(opt), "activate", G_CALLBACK(deny_gc_opt), gc);
 		gtk_widget_show(opt);
@@ -1434,7 +1441,7 @@
 
 	while (c) {
 		gc = c->data;
-		if (gc->prpl->set_permit_deny)
+		if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->set_permit_deny)
 			break;
 		gc = NULL;
 		c = c->next;
@@ -3211,7 +3218,7 @@
 	if(c->account)
 		smileys = get_proto_smileys(c->account->protocol);
 	else
-		smileys = get_proto_smileys(DEFAULT_PROTO);
+		smileys = get_proto_smileys(GAIM_PROTO_DEFAULT);
 
 	while(smileys) {
 		GtkIMHtmlSmiley *smiley = smileys->data;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/event.c	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,412 @@
+/**
+ * @file event.c Event API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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
+ */
+#include "event.h"
+
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * XXX - This is for debug_printf! Move out when debug_printf moved to debug.h
+ *          -- ChipX86
+ */
+#include "gaim.h"
+
+/**
+ * A signal callback.
+ */
+typedef struct
+{
+	void *handle;                /**< The plugin module handle.         */
+	GaimEvent event;             /**< The event type.                   */
+	void *function;              /**< The function to call.             */
+	void *data;                  /**< The data to pass to the function. */
+
+} GaimSignalCallback;
+
+/**
+ * A broadcast function.
+ */
+typedef struct
+{
+	GaimSignalBroadcastFunc func;
+	void *data;
+
+} GaimSignalBroadcaster;
+
+static GList *callbacks = NULL;
+static GList *broadcasters = NULL;
+
+void
+gaim_signal_connect(void *handle, GaimEvent event,
+					void *func, void *data)
+{
+	GaimSignalCallback *call;
+
+	g_return_if_fail(func != NULL);
+
+	call = g_new0(GaimSignalCallback, 1);
+	call->handle   = handle;
+	call->event    = event;
+	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(void *handle, GaimEvent event, void *func)
+{
+	GList *c, *next_c;
+	GaimSignalCallback *g = NULL;
+
+	g_return_if_fail(func != NULL);
+
+	for (c = callbacks; c != NULL; c = next_c) {
+		next_c = c->next;
+
+		g = (GaimSignalCallback *)c->data;
+
+		if (handle == g->handle && func == g->function) {
+			callbacks = g_list_remove(callbacks, c->data);
+			g_free(g);
+		}
+	}
+}
+
+void
+gaim_signals_disconnect_by_handle(void *handle)
+{
+	GList *c, *c_next;
+	GaimSignalCallback *g;
+
+	g_return_if_fail(handle != NULL);
+
+	debug_printf("%d callbacks to search\n", g_list_length(callbacks));
+
+	for (c = callbacks; c != NULL; c = c_next) {
+		c_next = c->next;
+		g = (GaimSignalCallback *)c->data;
+
+		if (g->handle == handle) {
+			callbacks = g_list_remove(callbacks, (gpointer)g);
+
+			debug_printf("Removing callback, %d remain\n",
+						 g_list_length(callbacks));
+		}
+	}
+}
+
+void
+gaim_signals_register_broadcast_func(GaimSignalBroadcastFunc func,
+									 void *data)
+{
+	GaimSignalBroadcaster *broadcaster;
+
+	g_return_if_fail(func != NULL);
+
+	broadcaster = g_new0(GaimSignalBroadcaster, 1);
+
+	broadcaster->func = func;
+	broadcaster->data = data;
+
+	broadcasters = g_list_append(broadcasters, broadcaster);
+}
+
+void
+gaim_signals_unregister_broadcast_func(GaimSignalBroadcastFunc func)
+{
+	GList *l;
+	GaimSignalBroadcaster *broadcaster;
+
+	g_return_if_fail(func != NULL);
+
+	for (l = broadcasters; l != NULL; l = l->next) {
+		broadcaster = l->data;
+
+		if (broadcaster->func == func) {
+			broadcasters = g_list_remove(broadcasters, broadcaster);
+
+			g_free(broadcaster);
+
+			break;
+		}
+	}
+}
+
+int
+gaim_event_broadcast(GaimEvent event, ...)
+{
+	GList *c;
+	GList *l;
+	GaimSignalCallback *g;
+	GaimSignalBroadcaster *broadcaster;
+	va_list arrg;
+	void    *arg1 = NULL, *arg2 = NULL, *arg3 = NULL, *arg4 = NULL;
+
+	for (c = callbacks; c != NULL; c = c->next) {
+		void (*cbfunc)(void *, ...);
+
+		g = (GaimSignalCallback *)c->data;
+
+		if (g->event == event && g->function != NULL) {
+			time_t time;
+			int id;
+			cbfunc = g->function;
+
+			va_start(arrg, event);
+
+			switch (event) {
+				/* no args */
+				case event_blist_update:
+				case event_quit:
+					cbfunc(g->data);
+					break;
+
+				/* one arg */
+				case event_signon:
+				case event_signoff:
+				case event_new_conversation:
+				case event_del_conversation:
+				case event_error:
+				case event_connecting:
+					arg1 = va_arg(arrg, void *);
+					cbfunc(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_set_info:
+				case event_draw_menu:
+				case event_got_typing:
+					arg1 = va_arg(arrg, void *);
+					arg2 = va_arg(arrg, void *);
+					cbfunc(arg1, arg2, g->data);
+					break;
+
+				case event_chat_leave:
+					arg1 = va_arg(arrg, void*);
+					id = va_arg(arrg, int);
+					cbfunc(arg1, id, g->data);
+					break;
+
+				/* three args */
+				case event_im_send:
+				case event_im_displayed_sent:
+				case event_away:
+					arg1 = va_arg(arrg, void *);
+					arg2 = va_arg(arrg, void *);
+					arg3 = va_arg(arrg, void *);
+					cbfunc(arg1, arg2, arg3, g->data);
+					break;
+
+				case event_chat_buddy_join:
+				case event_chat_buddy_leave:
+				case event_chat_send:
+				case event_chat_join:
+					arg1 = va_arg(arrg, void*);
+					id = va_arg(arrg, int);
+					arg3 = va_arg(arrg, void*);
+					cbfunc(arg1, id, arg3, g->data);
+					break;
+
+				case event_warned:
+					arg1 = va_arg(arrg, void*);
+					arg2 = va_arg(arrg, void*);
+					id = va_arg(arrg, int);
+					cbfunc(arg1, arg2, id, g->data);
+					break;
+
+				/* four args */
+				case event_im_recv:
+				case event_chat_invited:
+					arg1 = va_arg(arrg, void *);
+					arg2 = va_arg(arrg, void *);
+					arg3 = va_arg(arrg, void *);
+					arg4 = va_arg(arrg, void *);
+					cbfunc(arg1, arg2, arg3, arg4, g->data);
+					break;
+
+				case event_chat_recv:
+				case event_chat_send_invite:
+					arg1 = va_arg(arrg, void *);
+					id = va_arg(arrg, int);
+
+					arg3 = va_arg(arrg, void *);
+					arg4 = va_arg(arrg, void *);
+					cbfunc(arg1, id, arg3, arg4, g->data);
+					break;
+
+				/* five args */
+				case event_im_displayed_rcvd:
+					arg1 = va_arg(arrg, void *);
+					arg2 = va_arg(arrg, void *);
+					arg3 = va_arg(arrg, void *);
+					arg4 = va_arg(arrg, void *);
+					time = va_arg(arrg, time_t);
+					cbfunc(arg1, arg2, arg3, arg4, time, g->data);
+					break;
+
+				default:
+					debug_printf("unknown event %d\n", event);
+					break;
+			}
+
+			va_end(arrg);
+		}
+	}
+
+	for (l = broadcasters; l != NULL; l = l->next) {
+		broadcaster = l->data;
+
+		va_start(arrg, event);
+		broadcaster->func(event, broadcaster->data, arrg);
+	}
+
+	return 0;
+}
+
+const char *
+gaim_event_get_name(GaimEvent event)
+{
+	static char buf[128];
+
+	switch (event) {
+		case event_signon:
+			snprintf(buf, sizeof(buf), "event_signon");
+			break;
+		case event_signoff:
+			snprintf(buf, sizeof(buf), "event_signoff");
+			break;
+		case event_away:
+			snprintf(buf, sizeof(buf), "event_away");
+			break;
+		case event_back:
+			snprintf(buf, sizeof(buf), "event_back");
+			break;
+		case event_im_recv:
+			snprintf(buf, sizeof(buf), "event_im_recv");
+			break;
+		case event_im_send:
+			snprintf(buf, sizeof(buf), "event_im_send");
+			break;
+		case event_buddy_signon:
+			snprintf(buf, sizeof(buf), "event_buddy_signon");
+			break;
+		case event_buddy_signoff:
+			snprintf(buf, sizeof(buf), "event_buddy_signoff");
+			break;
+		case event_buddy_away:
+			snprintf(buf, sizeof(buf), "event_buddy_away");
+			break;
+		case event_buddy_back:
+			snprintf(buf, sizeof(buf), "event_buddy_back");
+			break;
+		case event_buddy_idle:
+			snprintf(buf, sizeof(buf), "event_buddy_idle");
+			break;
+		case event_buddy_unidle:
+			snprintf(buf, sizeof(buf), "event_buddy_unidle");
+			break;
+		case event_blist_update:
+			snprintf(buf, sizeof(buf), "event_blist_update");
+			break;
+		case event_chat_invited:
+			snprintf(buf, sizeof(buf), "event_chat_invited");
+			break;
+		case event_chat_join:
+			snprintf(buf, sizeof(buf), "event_chat_join");
+			break;
+		case event_chat_leave:
+			snprintf(buf, sizeof(buf), "event_chat_leave");
+			break;
+		case event_chat_buddy_join:
+			snprintf(buf, sizeof(buf), "event_chat_buddy_join");
+			break;
+		case event_chat_buddy_leave:
+			snprintf(buf, sizeof(buf), "event_chat_buddy_leave");
+			break;
+		case event_chat_recv:
+			snprintf(buf, sizeof(buf), "event_chat_recv");
+			break;
+		case event_chat_send:
+			snprintf(buf, sizeof(buf), "event_chat_send");
+			break;
+		case event_warned:
+			snprintf(buf, sizeof(buf), "event_warned");
+			break;
+		case event_error:
+			snprintf(buf, sizeof(buf), "event_error");
+			break;
+		case event_quit:
+			snprintf(buf, sizeof(buf), "event_quit");
+			break;
+		case event_new_conversation:
+			snprintf(buf, sizeof(buf), "event_new_conversation");
+			break;
+		case event_set_info:
+			snprintf(buf, sizeof(buf), "event_set_info");
+			break;
+		case event_draw_menu:
+			snprintf(buf, sizeof(buf), "event_draw_menu");
+			break;
+		case event_im_displayed_sent:
+			snprintf(buf, sizeof(buf), "event_im_displayed_sent");
+			break;
+		case event_im_displayed_rcvd:
+			snprintf(buf, sizeof(buf), "event_im_displayed_rcvd");
+			break;
+		case event_chat_send_invite:
+			snprintf(buf, sizeof(buf), "event_chat_send_invite");
+			break;
+		case event_got_typing:
+			snprintf(buf, sizeof(buf), "event_got_typing");
+			break;
+		case event_del_conversation:
+			snprintf(buf, sizeof(buf), "event_del_conversation");
+			break;
+		case event_connecting:
+			snprintf(buf, sizeof(buf), "event_connecting");
+			break;
+		default:
+			snprintf(buf, sizeof(buf), "event_unknown");
+			break;
+	}
+
+	return buf;
+}
+
+GList *
+gaim_get_callbacks(void)
+{
+	return callbacks;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/event.h	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,146 @@
+/**
+ * @file event.h Event API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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
+ */
+#ifndef _GAIM_EVENT_H_
+#define _GAIM_EVENT_H_
+
+#include <glib.h>
+
+/**
+ * Event types
+ */
+typedef enum gaim_event
+{
+	event_signon = 0,
+	event_signoff,
+	event_away,
+	event_back,
+	event_im_recv,
+	event_im_send,
+	event_buddy_signon,
+	event_buddy_signoff,
+	event_buddy_away,
+	event_buddy_back,
+	event_buddy_idle,
+	event_buddy_unidle,
+	event_blist_update,
+	event_chat_invited,
+	event_chat_join,
+	event_chat_leave,
+	event_chat_buddy_join,
+	event_chat_buddy_leave,
+	event_chat_recv,
+	event_chat_send,
+	event_warned,
+	event_error,
+	event_quit,
+	event_new_conversation,
+	event_set_info,
+	event_draw_menu,
+	event_im_displayed_sent,
+	event_im_displayed_rcvd,
+	event_chat_send_invite,
+	event_got_typing,
+	event_del_conversation,
+	event_connecting,
+	/* any others? it's easy to add... */
+
+} GaimEvent;
+
+typedef int (*GaimSignalBroadcastFunc)(GaimEvent event, void *data,
+									   va_list args);
+
+/**
+ * Connects a signal handler to a gaim event.
+ *
+ * @param module The optional module handle.
+ * @param event  The event to connect to.
+ * @param func   The callback function.
+ * @param data   The data to pass to the callback function.
+ *
+ * @see gaim_signal_disconnect()
+ */
+void gaim_signal_connect(void *module, GaimEvent event,
+						 void *func, void *data);
+
+/**
+ * Disconnects a signal handler from a gaim event.
+ *
+ * @param module The optional module handle.
+ * @param event  The event to disconnect from.
+ * @param func   The registered function to disconnect.
+ *
+ * @see gaim_signal_connect()
+ */
+void gaim_signal_disconnect(void *module, GaimEvent event,
+							void *func);
+
+/**
+ * Removes all callbacks associated with a handle.
+ *
+ * @param handle The handle.
+ */
+void gaim_signals_disconnect_by_handle(void *handle);
+
+/**
+ * Registers a function that re-broadcasts events.
+ *
+ * @param func The function.
+ * @param data Data to be passed to the callback.
+ */
+void gaim_signals_register_broadcast_func(GaimSignalBroadcastFunc func,
+										  void *data);
+
+/**
+ * Unregisters a function that re-broadcasts events.
+ *
+ * @param func The function.
+ */
+void gaim_signals_unregister_broadcast_func(GaimSignalBroadcastFunc func);
+
+/**
+ * Broadcasts an event to all registered signal handlers.
+ *
+ * @param event The event to broadcast
+ *
+ * @see gaim_signal_connect()
+ * @see gaim_signal_disconnect()
+ */
+int gaim_event_broadcast(GaimEvent event, ...);
+
+/**
+ * Returns a human-readable representation of an event name.
+ *
+ * @param event The event.
+ *
+ * @return A human-readable string of the name.
+ */
+const char *gaim_event_get_name(GaimEvent event);
+
+/**
+ * Returns a list of all signal callbacks.
+ *
+ * @return A list of all signal callbacks.
+ */
+GList *gaim_get_callbacks(void);
+
+#endif /* _GAIM_EVENT_H_ */
--- a/src/gaimrc.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/gaimrc.c	Fri Apr 25 06:47:33 2003 +0000
@@ -66,6 +66,7 @@
 static guint request_save_prefs = 0;
 static guint is_saving_prefs = 0;
 static guint request_load_prefs = 0;
+static guint prefs_initial_load = 0;
 guint proxy_info_is_from_gaimrc = 1; /* Only save proxy info if it
 				      * was loaded from the file
 				      * or otherwise explicitly requested */
@@ -534,24 +535,23 @@
 	fprintf(f, "}\n");
 }
 
-#ifdef GAIM_PLUGINS
 static void gaimrc_write_plugins(FILE *f)
 {
-	GList *pl = plugins;
-	struct gaim_plugin *p;
+	GList *pl;
+	GaimPlugin *p;
 
 	fprintf(f, "plugins {\n");
 
-	while (pl) {
+	for (pl = gaim_plugins_get_loaded(); pl != NULL; pl = pl->next) {
 		char *path;
 
-		p = (struct gaim_plugin *)pl->data;
+		p = (GaimPlugin *)pl->data;
 
-		path = escape_text2(p->path);
-		fprintf(f, "\tplugin { %s }\n", path);
-		free(path);
-
-		pl = pl->next;
+		if (p->info->type != GAIM_PLUGIN_PROTOCOL) {
+			path = escape_text2(p->path);
+			fprintf(f, "\tplugin { %s }\n", path);
+			free(path);
+		}
 	}
 
 	fprintf(f, "}\n");
@@ -562,7 +562,6 @@
 	struct parse parse_buffer;
 	struct parse *p;
 	char buf[4096];
-	GSList *load = NULL;
 
 	buf[0] = 0;
 
@@ -575,20 +574,10 @@
 
 		p = parse_line(buf, &parse_buffer);
 		if (!strcmp(p->option, "plugin")) {
-			load = g_slist_append(load, g_strdup(p->value[0]));
+			gaim_plugin_load(gaim_plugin_probe(p->value[0]));
 		}
 	}
-	/* this is such a fucked up hack. the reason we do this is because after
-	 * we load a plugin the gaimrc file gets rewrit. so we have to remember
-	 * which ones to load before loading them. */
-	while (load) {
-		if (load->data)
-			load_plugin(load->data);
-		g_free(load->data);
-		load = g_slist_remove(load, load->data);
-	}
 }
-#endif /* GAIM_PLUGINS */
 
 static struct gaim_account *gaimrc_read_user(FILE *f)
 {
@@ -613,7 +602,7 @@
 
 	account->user_info[0] = 0;
 	account->options = OPT_ACCT_REM_PASS;
-	account->protocol = DEFAULT_PROTO;
+	account->protocol = GAIM_PROTO_DEFAULT;
 	account->permit = account->deny = NULL;
 
 	if (!fgets(buf, sizeof(buf), f))
@@ -1027,19 +1016,21 @@
 		away_resend = 120;
 
 	if (misc_options & OPT_MISC_BUDDY_TICKER) {
-#ifdef GAIM_PLUGINS
-		gchar* buf;
+		if (gaim_plugins_enabled()) {
+			gchar* buf;
 
-		buf = g_strconcat(LIBDIR, G_DIR_SEPARATOR_S, 
+			buf = g_strconcat(LIBDIR, G_DIR_SEPARATOR_S, 
 #ifndef _WIN32
-				  "ticker.so",
+					  "ticker.so",
 #else
-				  "ticker.dll",
+					  "ticker.dll",
 #endif
-				  NULL);
-		load_plugin(buf);
-		g_free(buf);
-#endif
+					  NULL);
+
+			gaim_plugin_load(gaim_plugin_probe(buf));
+			g_free(buf);
+		}
+
 		misc_options &= ~OPT_MISC_BUDDY_TICKER;
 	} 
 }
@@ -1469,6 +1460,7 @@
 	char buf[1024];
 	int ver = 0;
 	debug_printf("load_prefs\n");
+
 	if (is_saving_prefs) {
 		request_load_prefs = 1;
 		debug_printf("currently saving, will request load\n");
@@ -1508,11 +1500,10 @@
 			case 2:
 				gaimrc_read_away(f);
 				break;
-#ifdef GAIM_PLUGINS
 			case 3:
-				gaimrc_read_plugins(f);
+				if (gaim_plugins_enabled())
+					gaimrc_read_plugins(f);
 				break;
-#endif
 			case 4:
 				gaimrc_read_pounce(f);
 				break;
@@ -1544,6 +1535,8 @@
 		set_defaults();
 		save_prefs();
 	}
+
+	prefs_initial_load = 1;
 }
 
 void save_prefs()
@@ -1553,6 +1546,9 @@
 	gchar *filename_temp;
 
 	debug_printf("enter save_prefs\n");  
+	if (!prefs_initial_load)
+		return;
+
 	if (is_loading_prefs) {
 		request_save_prefs = 1;
 		debug_printf("currently loading, will request save\n");
@@ -1576,9 +1572,10 @@
 		gaimrc_write_sounds(f);
 		gaimrc_write_away(f);
 		gaimrc_write_pounce(f);
-#ifdef GAIM_PLUGINS
-		gaimrc_write_plugins(f);
-#endif
+
+		if (gaim_plugins_enabled())
+			gaimrc_write_plugins(f);
+
 		gaimrc_write_proxy(f);
 		fclose(f);
 		if (rename(filename_temp, filename) < 0)
--- a/src/gtkconv.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/gtkconv.c	Fri Apr 25 06:47:33 2003 +0000
@@ -635,6 +635,7 @@
 static void
 menu_info_cb(GtkWidget *w, struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_connection *gc;
 	char *who;
 
@@ -642,20 +643,23 @@
 	who = g_object_get_data(G_OBJECT(w), "user_data");
 
 	if (gc != NULL) {
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 		/*
 		 * If there are special needs for getting info on users in
 		 * buddy chat "rooms"...
 		 */
-		if (gc->prpl->get_cb_info != NULL)
-			gc->prpl->get_cb_info(gc, gaim_chat_get_id(GAIM_CHAT(conv)), who);
+		if (prpl_info->get_cb_info != NULL)
+			prpl_info->get_cb_info(gc, gaim_chat_get_id(GAIM_CHAT(conv)), who);
 		else
-			gc->prpl->get_info(gc, who);
+			prpl_info->get_info(gc, who);
 	}
 }
 
 static void
 menu_away_cb(GtkWidget *w, struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_connection *gc;
 	char *who;
 
@@ -663,12 +667,14 @@
 	who = g_object_get_data(G_OBJECT(w), "user_data");
 
 	if (gc != NULL) {
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 		/*
 		 * May want to expand this to work similarly to menu_info_cb?
 		 */
 
-		if (gc->prpl->get_cb_away != NULL)
-			gc->prpl->get_cb_away(gc, gaim_chat_get_id(GAIM_CHAT(conv)), who);
+		if (prpl_info->get_cb_away != NULL)
+			prpl_info->get_cb_away(gc, gaim_chat_get_id(GAIM_CHAT(conv)), who);
 	}
 }
 
@@ -695,6 +701,7 @@
 right_click_chat_cb(GtkWidget *widget, GdkEventButton *event,
 					struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_gtk_conversation *gtkconv;
 	struct gaim_gtk_chat_pane *gtkchat;
 	struct gaim_connection *gc;
@@ -719,6 +726,9 @@
 	if (path == NULL)
 		return FALSE;
 
+	if (gc != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 	gtk_tree_selection_select_path(GTK_TREE_SELECTION(
 			gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path);
 
@@ -769,7 +779,7 @@
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
 		gtk_widget_show(button);
 
-		if (gc && gc->prpl->get_info) {
+		if (gc && prpl_info->get_info) {
 			button = gtk_menu_item_new_with_label(_("Info"));
 			g_signal_connect(G_OBJECT(button), "activate",
 							 G_CALLBACK(menu_info_cb), conv);
@@ -778,7 +788,7 @@
 			gtk_widget_show(button);
 		}
 
-		if (gc && gc->prpl->get_cb_away) {
+		if (gc && prpl_info->get_cb_away) {
 			button = gtk_menu_item_new_with_label(_("Get Away Msg"));
 			g_signal_connect(G_OBJECT(button), "activate",
 							 G_CALLBACK(menu_away_cb), conv);
@@ -1715,6 +1725,7 @@
 switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num,
 				gpointer user_data)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_window *win;
 	struct gaim_conversation *conv;
 	struct gaim_gtk_conversation *gtkconv;
@@ -1736,13 +1747,14 @@
 
 	if (gc != NULL) {
 		gtk_widget_set_sensitive(gtkwin->menu.insert_link, TRUE);
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
 	}
 
 	/* Update the menubar */
 	if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
 		gtk_widget_set_sensitive(gtkwin->menu.view_log, TRUE);
 		gtk_widget_set_sensitive(gtkwin->menu.insert_image,
-			(gc && gc->prpl->options & OPT_PROTO_IM_IMAGE));
+			(gc && prpl_info->options & OPT_PROTO_IM_IMAGE));
 
 		if (gtkwin->menu.send_as != NULL)
 			g_timeout_add(0, (GSourceFunc)update_send_as_selection, win);
@@ -2922,6 +2934,7 @@
 static GtkWidget *
 setup_chat_pane(struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_gtk_conversation *gtkconv;
 	struct gaim_gtk_chat_pane *gtkchat;
 	struct gaim_connection *gc;
@@ -2950,7 +2963,10 @@
 	gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, FALSE);
 	gtk_widget_show(vbox);
 
-	if (gc->prpl->options & OPT_PROTO_CHAT_TOPIC)
+	if (gc != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if (prpl_info->options & OPT_PROTO_CHAT_TOPIC)
 	{
 		hbox = gtk_hbox_new(FALSE, 0);
 		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
@@ -3746,6 +3762,7 @@
 static void
 update_convo_add_button(struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_gtk_conversation *gtkconv;
 	struct gaim_connection *gc;
 	GaimConversationType type;
@@ -3756,6 +3773,8 @@
 	gtkconv = GAIM_GTK_CONVERSATION(conv);
 	parent  = gtk_widget_get_parent(gtkconv->u.im->add);
 
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 	if (gaim_find_buddy(gc->account, gaim_conversation_get_name(conv))) {
 		gtkconv->u.im->add =
 			gaim_gtk_change_text(_("Remove"), gtkconv->u.im->add,
@@ -3764,7 +3783,7 @@
 			_("Remove the user from your buddy list"), NULL);
 
 		gtk_widget_set_sensitive(gtkconv->u.im->add,
-			(gc != NULL && gc->prpl->remove_buddy != NULL));
+			(gc != NULL && prpl_info->remove_buddy != NULL));
 	} else {
 		gtkconv->u.im->add =
 			gaim_gtk_change_text(_("Add"), gtkconv->u.im->add,
@@ -3773,7 +3792,7 @@
 			_("Add the user to your buddy list"), NULL);
 
 		gtk_widget_set_sensitive(gtkconv->u.im->add,
-			(gc != NULL && gc->prpl->add_buddy != NULL));
+			(gc != NULL && prpl_info->add_buddy != NULL));
 	}
 
 	g_signal_connect(G_OBJECT(gtkconv->u.im->add), "clicked",
@@ -3927,7 +3946,7 @@
 		strftime(mdate, sizeof(mdate), "%H:%M:%S", localtime(&mtime));
 
 	if(gc)
-		sml_attrib = g_strdup_printf("sml=\"%s\"", gc->prpl->name);
+		sml_attrib = g_strdup_printf("sml=\"%s\"", gc->prpl->info->name);
 
 	gtk_font_options ^= GTK_IMHTML_NO_COMMENTS;
 
@@ -5039,6 +5058,7 @@
 void
 gaim_gtkconv_update_buttons_by_protocol(struct gaim_conversation *conv)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	struct gaim_window *win;
 	struct gaim_gtk_window *gtkwin = NULL;
 	struct gaim_gtk_conversation *gtkconv;
@@ -5062,6 +5082,8 @@
 		}
 	}
 	else {
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 		gtk_widget_set_sensitive(gtkconv->send, TRUE);
 		if (win != NULL) {
 			gtk_widget_set_sensitive(gtkwin->menu.insert_link, TRUE);
@@ -5085,21 +5107,21 @@
 		}
 
 		gtk_widget_set_sensitive(gtkconv->info,
-								 (gc->prpl->get_info != NULL));
+								 (prpl_info->get_info != NULL));
 
 		gtk_widget_set_sensitive(gtkconv->toolbar.image,
-								 (gc->prpl->options & OPT_PROTO_IM_IMAGE));
+								 (prpl_info->options & OPT_PROTO_IM_IMAGE));
 
 		if (win != NULL && gaim_window_get_active_conversation(win) == conv) {
 			gtk_widget_set_sensitive(gtkwin->menu.insert_image,
-									 (gc->prpl->options & OPT_PROTO_IM_IMAGE));
+									 (prpl_info->options & OPT_PROTO_IM_IMAGE));
 		}
 
 		gtk_widget_set_sensitive(gtkconv->u.im->warn,
-								 (gc->prpl->warn != NULL));
+								 (prpl_info->warn != NULL));
 
 		gtk_widget_set_sensitive(gtkconv->u.im->block,
-								 (gc->prpl->add_permit != NULL));
+								 (prpl_info->add_permit != NULL));
 
 		update_convo_add_button(conv);
 	}
@@ -5111,16 +5133,16 @@
 			return;
 		}
 
-		gtk_widget_set_sensitive(gtkconv->send, (gc->prpl->chat_send != NULL));
+		gtk_widget_set_sensitive(gtkconv->send, (prpl_info->chat_send != NULL));
 
 		gtk_widget_set_sensitive(gtkconv->toolbar.image, FALSE);
 		/* gtk_widget_set_sensitive(gtkwin->menu.insert_image, FALSE); */
 
 		gtk_widget_set_sensitive(gtkconv->u.chat->whisper,
-								 (gc->prpl->chat_whisper != NULL));
+								 (prpl_info->chat_whisper != NULL));
 
 		gtk_widget_set_sensitive(gtkconv->u.chat->invite,
-								 (gc->prpl->chat_invite != NULL));
+								 (prpl_info->chat_invite != NULL));
 	}
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkplugin.c	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,43 @@
+/**
+ * @file plugins.h Conversation API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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
+ */
+#include "gtkplugin.h"
+#include <string.h>
+
+GtkWidget *
+gaim_gtk_plugin_get_config_frame(GaimPlugin *plugin)
+{
+	GaimGtkPluginUiInfo *ui_info;
+
+	g_return_val_if_fail(plugin != NULL, NULL);
+	g_return_val_if_fail(GAIM_IS_GTK_PLUGIN(plugin), NULL);
+
+	if (plugin->info->ui_info == NULL)
+		return NULL;
+
+	ui_info = GAIM_GTK_PLUGIN_UI_INFO(plugin);
+
+	if (ui_info->get_config_frame == NULL)
+		return NULL;
+
+	return ui_info->get_config_frame(plugin);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkplugin.h	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,61 @@
+/**
+ * @file gtkplugin.h GTK+ Plugin API
+ * @ingroup gtkui
+ *
+ * gaim
+ *
+ * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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
+ */
+#ifndef _GAIM_GTK_PLUGIN_H_
+#define _GAIM_GTK_PLUGIN_H_
+
+#include <gtk/gtk.h>
+#include "plugin.h"
+
+typedef struct _GaimGtkPluginUiInfo GaimGtkPluginUiInfo;
+
+/**
+ * A GTK+ UI structure for plugins.
+ */
+struct _GaimGtkPluginUiInfo
+{
+	GtkWidget *(*get_config_frame)(GaimPlugin *plugin);
+
+	void *iter;                                           /**< Reserved */
+};
+
+#define GAIM_GTK_PLUGIN_TYPE "gtk"
+
+#define GAIM_IS_GTK_PLUGIN(plugin) \
+	((plugin)->info != NULL && (plugin)->info->ui_info != NULL && \
+	 !strcmp((plugin)->info->ui_requirement, GAIM_GTK_PLUGIN_TYPE))
+
+#define GAIM_GTK_PLUGIN_UI_INFO(plugin) \
+	((GaimGtkPluginUiInfo *)(plugin)->info->ui_info)
+
+/**
+ * Returns the configuration frame widget for a GTK+ plugin, if one
+ * exists.
+ *
+ * @param plugin The plugin.
+ *
+ * @return The frame, if the plugin is a GTK+ plugin and provides a
+ *         configuration frame.
+ */
+GtkWidget *gaim_gtk_plugin_get_config_frame(GaimPlugin *plugin);
+
+#endif /* _GAIM_GTK_PLUGIN_H_ */
--- a/src/gtkpounce.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/gtkpounce.c	Fri Apr 25 06:47:33 2003 +0000
@@ -214,7 +214,7 @@
 pounce_user_menu(struct gaim_gtkpounce_dialog *dialog)
 {
 	struct gaim_account *account;
-	struct prpl *prpl;
+	GaimPlugin *prpl;
 	GtkWidget *opt_menu;
 	GtkWidget *menu;
 	GtkWidget *item;
@@ -228,10 +228,11 @@
 	for (l = gaim_accounts, count = 0; l != NULL; l = l->next, count++) {
 		account = (struct gaim_account *)l->data;
 
-		prpl = (struct prpl *)find_prpl(account->protocol);
+		prpl = gaim_find_prpl(account->protocol);
 
 		g_snprintf(buf, sizeof(buf), "%s (%s)", account->username,
-				   (prpl && prpl->name) ? prpl->name : _("Unknown"));
+				   (prpl && prpl->info->name)
+				   ? prpl->info->name : _("Unknown"));
 
 		item = gtk_menu_item_new_with_label(buf);
 		g_object_set_data(G_OBJECT(item), "user_data", account);
--- a/src/idle.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/idle.c	Fri Apr 25 06:47:33 2003 +0000
@@ -54,7 +54,7 @@
 #endif
 	int idle_time;
 
-	plugin_event(event_blist_update);
+	gaim_event_broadcast(event_blist_update);
 
 	time(&t);
 
--- a/src/list.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/list.c	Fri Apr 25 06:47:33 2003 +0000
@@ -120,9 +120,9 @@
 	buddy->uc = status;
 
 	if(!(status & UC_UNAVAILABLE))
-		plugin_event(event_buddy_back, buddy->account->gc, buddy->name);
+		gaim_event_broadcast(event_buddy_back, buddy->account->gc, buddy->name);
 	else
-		plugin_event(event_buddy_away, buddy->account->gc, buddy->name);
+		gaim_event_broadcast(event_buddy_away, buddy->account->gc, buddy->name);
 
 	if (ops)
 		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
@@ -133,7 +133,7 @@
 
 	if(buddy->present == GAIM_BUDDY_SIGNING_ON) {
 		buddy->present = GAIM_BUDDY_ONLINE;
-		plugin_event(event_buddy_signon, buddy->account->gc, buddy->name);
+		gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name);
 	} else if(buddy->present == GAIM_BUDDY_SIGNING_OFF) {
 		buddy->present = GAIM_BUDDY_OFFLINE;
 	}
@@ -152,11 +152,11 @@
 
 	if (!GAIM_BUDDY_IS_ONLINE(buddy) && presence) {
 		buddy->present = GAIM_BUDDY_SIGNING_ON;
-		plugin_event(event_buddy_signon, buddy->account->gc, buddy->name);
+		gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name);
 		do_timer = TRUE;
 	} else if(GAIM_BUDDY_IS_ONLINE(buddy) && !presence) {
 		buddy->present = GAIM_BUDDY_SIGNING_OFF;
-		plugin_event(event_buddy_signoff, buddy->account->gc, buddy->name);
+		gaim_event_broadcast(event_buddy_signoff, buddy->account->gc, buddy->name);
 		do_timer = TRUE;
 	}
 
@@ -813,7 +813,7 @@
 	} else {
 		char *g_screenname = get_screenname_filename(account->username);
 		char *file = gaim_user_dir();
-		int protocol = (account->protocol == PROTO_OSCAR) ? (isalpha(account->username[0]) ? PROTO_TOC : PROTO_ICQ): account->protocol;
+		int protocol = (account->protocol == GAIM_PROTO_OSCAR) ? (isalpha(account->username[0]) ? GAIM_PROTO_TOC : GAIM_PROTO_ICQ): account->protocol;
 
 		if (file != (char *)NULL) {
 			sprintf(path, "%s" G_DIR_SEPARATOR_S "%s.%d.blist", file, g_screenname, protocol);
--- a/src/log.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/log.c	Fri Apr 25 06:47:33 2003 +0000
@@ -295,32 +295,32 @@
 		switch (what) {
 		case log_signon:
 			g_snprintf(text, sizeof(text), _("+++ %s (%s) signed on @ %s"),
-				   gc->username, gc->prpl->name, full_date());
+				   gc->username, gc->prpl->info->name, full_date());
 			g_snprintf(html, sizeof(html), "<B>%s</B>", text);
 			break;
 		case log_signoff:
 			g_snprintf(text, sizeof(text), _("+++ %s (%s) signed off @ %s"),
-				   gc->username, gc->prpl->name, full_date());
+				   gc->username, gc->prpl->info->name, full_date());
 			g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text);
 			break;
 		case log_away:
 			g_snprintf(text, sizeof(text), _("+++ %s (%s) changed away state @ %s"),
-				   gc->username, gc->prpl->name, full_date());
+				   gc->username, gc->prpl->info->name, full_date());
 			g_snprintf(html, sizeof(html), "<FONT COLOR=OLIVE>%s</FONT>", text);
 			break;
 		case log_back:
 			g_snprintf(text, sizeof(text), _("+++ %s (%s) came back @ %s"),
-				   gc->username, gc->prpl->name, full_date());
+				   gc->username, gc->prpl->info->name, full_date());
 			g_snprintf(html, sizeof(html), "%s", text);
 			break;
 		case log_idle:
 			g_snprintf(text, sizeof(text), _("+++ %s (%s) became idle @ %s"),
-				   gc->username, gc->prpl->name, full_date());
+				   gc->username, gc->prpl->info->name, full_date());
 			g_snprintf(html, sizeof(html), "<FONT COLOR=GRAY>%s</FONT>", text);
 			break;
 		case log_unidle:
 			g_snprintf(text, sizeof(text), _("+++ %s (%s) returned from idle @ %s"),
-				   gc->username, gc->prpl->name, full_date());
+				   gc->username, gc->prpl->info->name, full_date());
 			g_snprintf(html, sizeof(html), "%s", text);
 			break;
 		case log_quit:
@@ -332,33 +332,33 @@
 		switch (what) {
 		case log_signon:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) signed on @ %s"),
-				   gc->username, gc->prpl->name, gaim_get_buddy_alias(who), who->name, full_date());
+				   gc->username, gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, full_date());
 			g_snprintf(html, sizeof(html), "<B>%s</B>", text);
 			break;
 		case log_signoff:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) signed off @ %s"),
-				   gc->username, gc->prpl->name, gaim_get_buddy_alias(who), who->name, full_date());
+				   gc->username, gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, full_date());
 			g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text);
 			break;
 		case log_away:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) went away @ %s"),
-				   gc->username, gc->prpl->name, gaim_get_buddy_alias(who), who->name, full_date());
+				   gc->username, gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, full_date());
 			g_snprintf(html, sizeof(html), "<FONT COLOR=OLIVE>%s</FONT>", text);
 			break;
 		case log_back:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) came back @ %s"),
-				   gc->username, gc->prpl->name, gaim_get_buddy_alias(who), who->name, full_date());
+				   gc->username, gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, full_date());
 			g_snprintf(html, sizeof(html), "%s", text);
 			break;
 		case log_idle:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s (%s) became idle @ %s"),
-				   gc->username, gc->prpl->name, gaim_get_buddy_alias(who), who->name, full_date());
+				   gc->username, gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, full_date());
 			g_snprintf(html, sizeof(html), "<FONT COLOR=GRAY>%s</FONT>", text);
 			break;
 		case log_unidle:
 			g_snprintf(text, sizeof(text),
 				   _("%s (%s) reported that %s (%s) returned from idle @ %s"), gc->username,
-				   gc->prpl->name, gaim_get_buddy_alias(who), who->name, full_date());
+				   gc->prpl->info->name, gaim_get_buddy_alias(who), who->name, full_date());
 			g_snprintf(html, sizeof(html), "%s", text);
 			break;
 		default:
@@ -370,33 +370,33 @@
 		switch (what) {
 		case log_signon:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s signed on @ %s"),
-				   gc->username, gc->prpl->name, who->name, full_date());
+				   gc->username, gc->prpl->info->name, who->name, full_date());
 			g_snprintf(html, sizeof(html), "<B>%s</B>", text);
 			break;
 		case log_signoff:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s signed off @ %s"),
-				   gc->username, gc->prpl->name, who->name, full_date());
+				   gc->username, gc->prpl->info->name, who->name, full_date());
 			g_snprintf(html, sizeof(html), "<I><FONT COLOR=GRAY>%s</FONT></I>", text);
 			break;
 		case log_away:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s went away @ %s"),
-				   gc->username, gc->prpl->name, who->name, full_date());
+				   gc->username, gc->prpl->info->name, who->name, full_date());
 			g_snprintf(html, sizeof(html), "<FONT COLOR=OLIVE>%s</FONT>", text);
 			break;
 		case log_back:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s came back @ %s"),
-				   gc->username, gc->prpl->name, who->name, full_date());
+				   gc->username, gc->prpl->info->name, who->name, full_date());
 			g_snprintf(html, sizeof(html), "%s", text);
 			break;
 		case log_idle:
 			g_snprintf(text, sizeof(text), _("%s (%s) reported that %s became idle @ %s"),
-				   gc->username, gc->prpl->name, who->name, full_date());
+				   gc->username, gc->prpl->info->name, who->name, full_date());
 			g_snprintf(html, sizeof(html), "<FONT COLOR=GRAY>%s</FONT>", text);
 			break;
 		case log_unidle:
 			g_snprintf(text, sizeof(text),
 				   _("%s (%s) reported that %s returned from idle @ %s"), gc->username,
-				   gc->prpl->name, who->name, full_date());
+				   gc->prpl->info->name, who->name, full_date());
 			g_snprintf(html, sizeof(html), "%s", text);
 			break;
 		default:
--- a/src/main.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/main.c	Fri Apr 25 06:47:33 2003 +0000
@@ -103,13 +103,15 @@
 };
 #endif
 
+STATIC_PROTO_INIT
+
 void do_quit()
 {
 	/* captain's log, stardate... */
 	system_log(log_quit, NULL, NULL, OPT_LOG_BUDDY_SIGNON | OPT_LOG_MY_SIGNON);
 
 	/* the self destruct sequence has been initiated */
-	plugin_event(event_quit);
+	gaim_event_broadcast(event_quit);
 
 	/* transmission ends */
 	signoff_all();
@@ -117,15 +119,16 @@
 	/* record what we have before we blow it away... */
 	save_prefs();
 
-#ifdef GAIM_PLUGINS
-	/* jettison cargo */
-	remove_all_plugins();
-#endif
+	debug_printf("Unloading all plugins\n");
+	gaim_plugins_unload_all();
 
+	/* XXX */
+#if 0
 #ifdef USE_PERL
 	/* yup, perl too */
 	perl_end();
 #endif
+#endif
 
 #ifdef USE_SM
 	/* unplug */
@@ -184,7 +187,9 @@
 
 	account = gaim_account_find(username, -1);
 	if (!account)
-		account = gaim_account_new(username, DEFAULT_PROTO, OPT_ACCT_REM_PASS);
+		account = gaim_account_new(username, GAIM_PROTO_DEFAULT,
+								   OPT_ACCT_REM_PASS);
+
 	g_snprintf(account->password, sizeof account->password, "%s", password);
 	save_prefs();
 	serv_login(account);
@@ -437,9 +442,9 @@
 	default:
 		debug_printf("caught signal %d\n", sig);
 		signoff_all(NULL, NULL);
-#ifdef GAIM_PLUGINS
-		remove_all_plugins();
-#endif
+
+		gaim_plugins_unload_all();
+
 		if (gtk_main_level())
 			gtk_main_quit();
 		core_quit();
@@ -567,7 +572,7 @@
 	if (!account) {		/* new user */
 		account = g_new0(struct gaim_account, 1);
 		g_snprintf(account->username, sizeof(account->username), "%s", name);
-		account->protocol = DEFAULT_PROTO;
+		account->protocol = GAIM_PROTO_DEFAULT;
 		gaim_accounts = g_slist_prepend(gaim_accounts, account);
 	} else {		/* user already exists */
 		gaim_accounts = g_slist_remove(gaim_accounts, account);
@@ -609,6 +614,7 @@
 	int opt_acct = 0, opt_help = 0, opt_version = 0, opt_login = 0, opt_nologin = 0, dologin_ret = -1;
 	char *opt_user_arg = NULL, *opt_login_arg = NULL;
 	char *opt_session_arg = NULL;
+	char *plugin_search_paths[3];
 #if HAVE_SIGNAL_H
 	int sig_indx;	/* for setting up signal catching */
 	sigset_t sigset;
@@ -854,11 +860,19 @@
 		printf("Gaim %s\n",VERSION);
 		return 0;
 	}
-	
-#if GAIM_PLUGINS || USE_PERL
-	gaim_probe_plugins();
-#endif
-		
+
+	plugin_search_paths[0] = LIBDIR;
+	plugin_search_paths[1] = gaim_user_dir();
+	plugin_search_paths[2] = g_strdup_printf("%s/plugins", gaim_user_dir());
+
+	gaim_plugins_set_search_paths(sizeof(plugin_search_paths) /
+								  sizeof(*plugin_search_paths),
+								  plugin_search_paths);
+
+	g_free(plugin_search_paths[2]);
+
+	gaim_plugins_probe(NULL);
+
 #ifdef _WIN32
 	/* Various win32 initializations */
 	wgaim_init();
--- a/src/module.c	Thu Apr 24 03:52:25 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,672 +0,0 @@
-/*
- * 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
-
-#include "gaim.h"
-#include "prpl.h"
-
-#include <string.h>
-#include <sys/time.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef _WIN32
-#include "win32dep.h"
-#endif
-
-/* ------------------ Global Variables ----------------------- */
-
-GList *plugins = NULL;
-GList *probed_plugins = NULL;
-GList *callbacks = NULL;
-
-char *last_dir = NULL;
-
-/* --------------- Function Declarations --------------------- */
-
-struct gaim_plugin *  load_plugin(const char *);
-#ifdef GAIM_PLUGINS
-              void  unload_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 *);
-#endif
-/* ------------------ Code Below ---------------------------- */
-
-static int is_so_file(char *filename, char *ext)
-{
-	int len;
-	if (!filename) return 0;
-	if (!filename[0]) return 0;
-	len = strlen(filename);
-	len -= strlen(ext);
-	if (len < 0) return 0;
-	return (!strncmp(filename + len, ext, strlen(ext)));
-}
-
-void gaim_probe_plugins() {
-	GDir *dir;
-	const gchar *file;
-	gchar *path;
-	/*struct gaim_plugin_description *plugdes;*/
-	struct gaim_plugin *plug;
-	char *probedirs[3];
-	int l;
-#if GAIM_PLUGINS     
-	char *(*gaim_plugin_init)(GModule *);
-	char *(*gaim_prpl_init)(struct prpl *);
-	char *(*cfunc)();
-	struct prpl * new_prpl;
-	struct gaim_plugin_description *(*desc)();
-	GModule *handle;
-#endif
-
-	probedirs[0] = LIBDIR;
-	probedirs[1] = gaim_user_dir();
-	probedirs[2] = 0;
-
-	for (l=0; probedirs[l]; l++) {
-		dir = g_dir_open(probedirs[l], 0, NULL);
-		if (dir) {
-			while ((file = g_dir_read_name(dir))) {
-#ifdef GAIM_PLUGINS
-				if (is_so_file((char*)file, 
-#ifndef _WIN32
-					       ".so"
-#else
-					       ".dll"
-#endif
-					       ) && g_module_supported()) {
-					path = g_build_filename(probedirs[l], file, NULL);
-					handle = g_module_open(path, 0);
-					if (!handle) {
-						debug_printf("%s is unloadable: %s\n", file, g_module_error());
-						g_free(path);
-						continue;
-					}
-					if (g_module_symbol(handle, "gaim_prpl_init", (gpointer *)&gaim_prpl_init)) {
-						plug = g_new0(struct gaim_plugin, 1);
-						g_snprintf(plug->path, sizeof(plug->path), path);
-						plug->type = plugin;
-
-						new_prpl = g_new0(struct prpl, 1);
-						new_prpl->plug = plug;
-						gaim_prpl_init(new_prpl);
-						if (new_prpl->protocol == PROTO_ICQ ||
-						    find_prpl(new_prpl->protocol)) {
-							/* Nothing to see here--move along, move along */
-							unload_protocol(new_prpl);
-							g_free(path);
-							continue;
-						}
-						protocols = g_slist_insert_sorted(protocols, new_prpl, (GCompareFunc)proto_compare);
-						g_module_close(handle);
-						g_free(path);
-						continue;
-					}
-					
-					if (!g_module_symbol(handle, "gaim_plugin_init", (gpointer *)&gaim_plugin_init)) {
-						debug_printf("%s is unloadable %s\n", file, g_module_error());
-						g_module_close(handle);
-						g_free(path);
-						continue;
-					}
-					plug = g_new0(struct gaim_plugin, 1);
-					g_snprintf(plug->path, sizeof(plug->path), path);
-					plug->type = plugin;
-					g_free(path);
-					if (g_module_symbol(handle, "gaim_plugin_desc", (gpointer *)&desc)) {
-						memcpy(&(plug->desc), desc(), sizeof(plug->desc));
-					} else {
-						if (g_module_symbol(handle, "name", (gpointer *)&cfunc)) {
-							plug->desc.name = g_strdup(cfunc());
-						}
-						if (g_module_symbol(handle, "description", (gpointer *)&cfunc)) {
-							plug->desc.description = g_strdup(cfunc());
-						}
-					}
-					probed_plugins = g_list_append(probed_plugins, plug);
-					g_module_close(handle);
-				}
-#endif
-#ifdef USE_PERL
-				if (is_so_file((char*)file, ".pl")) {
-					path = g_build_filename(probedirs[l], file, NULL);
-					plug = probe_perl(path);
-					if (plug) 
-						probed_plugins = g_list_append(probed_plugins, plug);
-					g_free(path);
-				}
-#endif
-			}
-			g_dir_close(dir);
-		}
-	}
-}
-
-#ifdef GAIM_PLUGINS
-struct gaim_plugin *load_plugin(const char *filename)
-{
-	struct gaim_plugin *plug=NULL;
-	struct gaim_plugin_description *desc;
-	struct gaim_plugin_description *(*gaim_plugin_desc)();
-	char *(*cfunc)();
-	/*GList *c = plugins;*/
-	GList *p = probed_plugins;
-	char *(*gaim_plugin_init)(GModule *);
-	char *error=NULL;
-	char *retval;
-	gboolean newplug = FALSE;
-
-	if (!g_module_supported())
-		return NULL;
-	if (!filename || !strlen(filename))
-		return NULL;
-
-#ifdef USE_PERL
-	if (is_so_file((char*)filename, ".pl")) {
-		/* perl_load_file is returning an int.. this should be fixed */
-		return (struct gaim_plugin *)perl_load_file((char*)filename);
-	}
-#endif
-
-	while (filename && p) {
-		plug = (struct gaim_plugin *)p->data;
-		if (!strcmp(filename, plug->path)) 
-			break;
-		p = p->next;
-	}
-	
-	if (plug && plug->handle) {
-		return plug;
-	}
-	    
-	if (!plug) {
-		plug = g_new0(struct gaim_plugin, 1);
-		g_snprintf(plug->path, sizeof(plug->path), filename);
-		newplug = TRUE;
-	}
-			
-	debug_printf("Loading %s\n", filename);
-	plug->handle = g_module_open(filename, 0);
-	if (!plug->handle) {
-		error = (char *)g_module_error();
-		plug->handle = NULL;
-		g_snprintf(plug->error, sizeof(plug->error), error);
-		return NULL;
-	}
-	
-	if (!g_module_symbol(plug->handle, "gaim_plugin_init", (gpointer *)&gaim_plugin_init)) {
-		g_module_close(plug->handle);
-		plug->handle = NULL;
-		g_snprintf(plug->error, sizeof(plug->error), error);
-		return NULL;
-	}
-
-	plug->error[0] = '\0';
-	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("Gaim was unable to load your plugin.", retval, GAIM_ERROR);
-		g_module_close(plug->handle);
-		plug->handle = NULL;
-		return NULL;
-	}
-
-	plugins = g_list_append(plugins, plug);
-
-	if (newplug) {
-		g_snprintf(plug->path, sizeof(plug->path), filename);
-		if (g_module_symbol(plug->handle, "gaim_plugin_desc", (gpointer *)&gaim_plugin_desc)) {
-			desc = gaim_plugin_desc();
-			plug->desc.name = desc->name;
-		} else {
-			if (g_module_symbol(plug->handle, "name", (gpointer *)&cfunc)) {
-				plug->desc.name = g_strdup(cfunc());
-			}
-			if (g_module_symbol(plug->handle, "description", (gpointer *)&cfunc)) {
-				plug->desc.description = g_strdup(cfunc());
-			}
-		}
-		probed_plugins = g_list_append(probed_plugins, plug);
-	}
-	
-	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));
-
-	/* cancel any pending dialogs the plugin has */
-	do_ask_cancel_by_handle(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);
-	p->handle = NULL;
-	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)
-{
-	GList *pl = plugins;
-	struct gaim_plugin *p = NULL;
-	void (*gaim_plugin_remove)();
-
-	while (pl) {
-		p = pl->data;
-		if (p->handle == handle)
-			break;
-		pl = pl->next;
-	}
-	if (!pl)
-		return;
-
-	debug_printf("Unloading %s\n", g_module_name(p->handle));
-
-	/* cancel any pending dialogs the plugin has */
-	do_ask_cancel_by_handle(p->handle);
-
-	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);
-	/* XXX CUI need to tell UI what happened, but not like this 
-	   update_show_plugins(); */
-
-	g_timeout_add(5000, unload_timeout, handle);
-}
-
-/* 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 */
-
-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;
-	case event_got_typing:
-		sprintf(buf, "event_got_typing");
-		break;
-	case event_del_conversation:
-		sprintf(buf, "event_del_conversation");
-		break;
-	case event_connecting:
-		sprintf(buf, "event_connecting");
-		break;
-	default:
-		sprintf(buf, "event_unknown");
-		break;
-	}
-	return buf;
-}
-
-int plugin_event(enum gaim_event event, ...)
-{
-#ifdef GAIM_PLUGINS
-	GList *c = callbacks;
-	struct gaim_callback *g;
-#endif
-	va_list arrg;
-	void    *arg1 = NULL, 
-		*arg2 = NULL,
-		*arg3 = NULL, 
-		*arg4 = NULL,
-		*arg5 = NULL;
-
-
-	/*	debug_printf("%s\n", event_name(event)); */
-
-#ifdef GAIM_PLUGINS
-	while (c) {
-		void (*cbfunc)(void *, ...);
-		
-		g = (struct gaim_callback *)c->data;
-		if (g->event == event && g->function != NULL) {
-			cbfunc=g->function;
-			va_start(arrg, event);
-			switch (event) {
-
-				/* no args */
-			case event_blist_update:
-			case event_quit:
-				cbfunc(g->data);
-				break;
-
-				/* one arg */
-			case event_signon:
-			case event_signoff:
-			case event_new_conversation:
-			case event_del_conversation:
-			case event_error:
-			case event_connecting:
-				arg1 = va_arg(arrg, void *);
-				cbfunc(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_set_info:
-			case event_draw_menu:
-			case event_got_typing:
-				arg1 = va_arg(arrg, void *);
-				arg2 = va_arg(arrg, void *);
-				cbfunc(arg1, arg2, g->data);
-				break;
-			case event_chat_leave:
-				{
-					int id;
-					arg1 = va_arg(arrg, void*);
-					id = va_arg(arrg, int);
-					cbfunc(arg1, id, g->data);
-				}
-				break;
-				/* three args */
-			case event_im_send:
-			case event_im_displayed_sent:
-			case event_away:
-				arg1 = va_arg(arrg, void *);
-				arg2 = va_arg(arrg, void *);
-				arg3 = va_arg(arrg, void *);
-				cbfunc(arg1, arg2, arg3, g->data);
-				break;
-			case event_chat_buddy_join:
-			case event_chat_buddy_leave:
-			case event_chat_send:
-			case event_chat_join:
-				{
-					int id;
-					arg1 = va_arg(arrg, void*);
-					id = va_arg(arrg, int);
-					arg3 = va_arg(arrg, void*);
-					cbfunc(arg1, id, arg3, g->data);
-				}
-				break;
-			case event_warned:
-				{
-					int id;
-					arg1 = va_arg(arrg, void*);
-					arg2 = va_arg(arrg, void*);
-					id = va_arg(arrg, int);
-					cbfunc(arg1, arg2, id, g->data);
-				}
-				break;
-				/* four args */
-			case event_im_recv:
-			case event_chat_invited:
-				arg1 = va_arg(arrg, void *);
-				arg2 = va_arg(arrg, void *);
-				arg3 = va_arg(arrg, void *);
-				arg4 = va_arg(arrg, void *);
-				cbfunc(arg1, arg2, arg3, arg4, g->data);
-				break;
-			case event_chat_recv:
-			case event_chat_send_invite:
-				{
-					int id;
-					arg1 = va_arg(arrg, void *);
-					id = va_arg(arrg, int);
-
-					arg3 = va_arg(arrg, void *);
-					arg4 = va_arg(arrg, void *);
-					cbfunc(arg1, id, arg3, arg4, g->data);
-				}
-				break;
-				/* five args */
-			case event_im_displayed_rcvd:
-				{
-					time_t time;
-					arg1 = va_arg(arrg, void *);
-					arg2 = va_arg(arrg, void *);
-					arg3 = va_arg(arrg, void *);
-					arg4 = va_arg(arrg, void *);
-					time = va_arg(arrg, time_t);
-					cbfunc(arg1, arg2, arg3, arg4, time, g->data);
-				}
-				break;
-				default:
-				debug_printf("unknown event %d\n", event);
-				break;
-			}
-			va_end(arrg);
-		}
-		c = c->next;
-	}
-#endif /* GAIM_PLUGINS */
-#ifdef USE_PERL
-	va_start(arrg, event);
-	arg1 = va_arg(arrg, void *);
-	arg2 = va_arg(arrg, void *);
-	arg3 = va_arg(arrg, void *);
-	arg4 = va_arg(arrg, void *);
-	arg5 = va_arg(arrg, void *);
-	return perl_event(event, arg1, arg2, arg3, arg4, arg5);
-#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 (p->type == plugin) {
-			if (g_module_symbol(p->handle, "gaim_plugin_remove", (gpointer *)&gaim_plugin_remove))
-				gaim_plugin_remove();
-		}
-		if(p)
-			g_free(p);
-		c = c->next;
-	}
-}
-#endif
--- a/src/multi.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/multi.c	Fri Apr 25 06:47:33 2003 +0000
@@ -133,7 +133,7 @@
 	struct gaim_connection *gc = g_new0(struct gaim_connection, 1);
 	gc->edittype = EDIT_GC;
 	gc->protocol = account->protocol;
-	gc->prpl = find_prpl(account->protocol);
+	gc->prpl = gaim_find_prpl(account->protocol);
 	g_snprintf(gc->username, sizeof(gc->username), "%s", account->username);
 	g_snprintf(gc->password, sizeof(gc->password), "%s", account->password);
 	gc->keepalive = 0;
@@ -212,9 +212,9 @@
 
 static char *proto_name(int proto)
 {
-	struct prpl *p = find_prpl(proto);
-	if (p && p->name)
-		return p->name;
+	GaimPlugin *p = gaim_find_prpl(proto);
+	if (p && p->info->name)
+		return p->info->name;
 	else
 		return "Unknown";
 }
@@ -366,14 +366,16 @@
 }
 
 static void process_login_opts(struct mod_account *ma) {
-	struct prpl *p = find_prpl(ma->protocol);
+	GaimPlugin *p = gaim_find_prpl(ma->protocol);
 	const char *entry_text;
 	char *username = g_strdup(gtk_entry_get_text(GTK_ENTRY(ma->name)));
 	char *tmp;
 	GList *entries = ma->user_split_entries;
 	GList *user_splits = NULL;
+
 	if(p)
-		user_splits = p->user_splits;
+		user_splits = GAIM_PLUGIN_PROTOCOL_INFO(p)->user_splits;
+
 	while(user_splits) {
 		GtkWidget *entry = entries->data;
 		struct proto_user_split *pus = user_splits->data;
@@ -404,7 +406,8 @@
 	GList *tmp;
 	const char *txt;
 	struct gaim_account *a;
-	struct prpl *p = find_prpl(ma->protocol);
+	GaimPlugin *p = gaim_find_prpl(ma->protocol);
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	GtkTreeIter iter;
 	int proxytype;
 
@@ -478,21 +481,19 @@
 		ma->account->gpi = gpi;
 	}
 
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
+
 	/*
 	 * See if user registration is supported/required
 	 */
-	if(!p) {
+	if (!p) {
 		/* TBD: error dialog here! (This should never happen, you know...) */
 		fprintf(stderr, "dbg: couldn't find protocol for protocol number %d!\n", ma->protocol);
 		fflush(stderr);
 	} else {
-		if(p->register_user != NULL &&
+		if (prpl_info->register_user != NULL &&
 		   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ma->register_user)) == TRUE) {
-			ref_protocol(p);
-			p->register_user(a);
-			/* we don't unref the protocol because register user has callbacks
-			 * that need to get called first, then they will unref the protocol
-			 * appropriately */
+			prpl_info->register_user(a);
 		}
 	}
 
@@ -519,13 +520,13 @@
 static void set_prot(GtkWidget *opt, int proto)
 {
 	struct mod_account *ma = g_object_get_data(G_OBJECT(opt), "mod_account");
-	struct prpl *p;
+	GaimPlugin *p;
 	if (ma->protocol != proto) {
 		int i;
 
 		for (i = 0; i < 7; i++)
 			ma->proto_opt[i][0] = '\0';
-		p = find_prpl(ma->protocol);
+		p = gaim_find_prpl(ma->protocol);
 
 		process_login_opts(ma);
 
@@ -543,11 +544,12 @@
 
 static GtkWidget *make_protocol_menu(GtkWidget *box, struct mod_account *ma)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	GtkWidget *optmenu;
 	GtkWidget *menu;
 	GtkWidget *opt;
-	GSList *p = protocols;
-	struct prpl *e;
+	GSList *p;
+	GaimPlugin *e;
 	int count = 0;
 	gboolean found = FALSE;
 
@@ -557,22 +559,23 @@
 
 	menu = gtk_menu_new();
 
-	while (p) {
-		e = (struct prpl *)p->data;
-		if (e->protocol == ma->protocol)
+	for (p = protocols; p != NULL; p = p->next) {
+		e = (GaimPlugin *)p->data;
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(e);
+
+		if (prpl_info->protocol == ma->protocol)
 			found = TRUE;
 		if (!found)
 			count++;
-		if (e->name)
-			opt = gtk_menu_item_new_with_label(e->name);
+		if (e->info->name)
+			opt = gtk_menu_item_new_with_label(e->info->name);
 		else
 			opt = gtk_menu_item_new_with_label("Unknown");
 		g_object_set_data(G_OBJECT(opt), "mod_account", ma);
 		g_signal_connect(GTK_OBJECT(opt), "activate",
-				   G_CALLBACK(set_prot), (void *)e->protocol);
+				   G_CALLBACK(set_prot), (void *)prpl_info->protocol);
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), opt);
 		gtk_widget_show(opt);
-		p = p->next;
 	}
 
 	gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
@@ -693,7 +696,7 @@
 	GList *user_splits = NULL;
 	GList *split_entries;
 
-	struct prpl *p;
+	GaimPlugin *p;
 
 	char *username = NULL;
 	char *start;
@@ -724,9 +727,10 @@
 	hbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 
-	p = find_prpl(ma->protocol);
+	p = gaim_find_prpl(ma->protocol);
+
 	if(p)
-		user_splits = p->user_splits;
+		user_splits = GAIM_PLUGIN_PROTOCOL_INFO(p)->user_splits;
 
 	label = gtk_label_new(_("Screenname:"));
 	gtk_size_group_add_widget(ma->sg, label);
@@ -788,8 +792,8 @@
 
 	gtk_widget_show_all(ma->login_frame);
 
-	if(p)
-		user_splits = g_list_last(p->user_splits);
+	if (p)
+		user_splits = g_list_last(GAIM_PLUGIN_PROTOCOL_INFO(p)->user_splits);
 
 	username = g_strdup(ma->username);
 	split_entries = g_list_last(ma->user_split_entries);
@@ -814,7 +818,7 @@
 	gtk_entry_set_text(GTK_ENTRY(ma->pass), ma->password);
 	g_free(username);
 
-	if (p && (p->options & OPT_PROTO_NO_PASSWORD)) {
+	if (p && (GAIM_PLUGIN_PROTOCOL_INFO(p)->options & OPT_PROTO_NO_PASSWORD)) {
 		gtk_widget_hide(ma->pwdbox);
 		gtk_widget_hide(ma->rempass);
 	}
@@ -833,8 +837,9 @@
 
 	GtkWidget *vbox;
 	GtkWidget *frame;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 
-	struct prpl *p = find_prpl(ma->protocol);
+	GaimPlugin *p = gaim_find_prpl(ma->protocol);
 
 	if(ma->user_frame)
 		gtk_widget_destroy(ma->user_frame);
@@ -858,19 +863,26 @@
 		return;
 	}
 
-	if (!(p->options & OPT_PROTO_MAIL_CHECK))
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
+
+	if (!(prpl_info->options & OPT_PROTO_MAIL_CHECK))
 		gtk_widget_hide(ma->checkmail);
-	if (!(p->options & OPT_PROTO_BUDDY_ICON))
+	if (!(prpl_info->options & OPT_PROTO_BUDDY_ICON))
 		gtk_widget_hide(ma->iconsel);
 
-	if ((p->options & OPT_PROTO_BUDDY_ICON) || (p->options & OPT_PROTO_MAIL_CHECK))
+	if ((prpl_info->options & OPT_PROTO_BUDDY_ICON) ||
+		(prpl_info->options & OPT_PROTO_MAIL_CHECK)) {
+
 		return;
+	}
+
 	gtk_widget_hide(ma->user_frame);
 }
 
 static void generate_protocol_options(struct mod_account *ma, GtkWidget *box)
 {
-	struct prpl *p = find_prpl(ma->protocol);
+	GaimPlugin *p = gaim_find_prpl(ma->protocol);
+	GaimPluginProtocolInfo *prpl_info = NULL;
 
 	GList *op, *tmp;
 
@@ -894,15 +906,17 @@
 	if (!p)
 		return;
 
-	if (!p->user_opts)
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
+
+	if (!prpl_info->user_opts)
 		return;
 
-	tmp = op = p->user_opts;
+	tmp = op = prpl_info->user_opts;
 
 	if (!op)
 		return;
 
-	g_snprintf(buf, sizeof(buf), _("%s Options"), p->name);
+	g_snprintf(buf, sizeof(buf), _("%s Options"), p->info->name);
 	frame = make_frame(box, buf);
 
 	/* BLEH */
@@ -944,7 +958,7 @@
 		op = op->next;
 	}
 
-	if(p->register_user != NULL) {
+	if(prpl_info->register_user != NULL) {
 		ma->register_user = gtk_check_button_new_with_label(_("Register with server"));
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ma->register_user), FALSE);
 		gtk_box_pack_start(GTK_BOX(vbox), ma->register_user, FALSE, FALSE, 0);
@@ -1154,10 +1168,11 @@
 		if (a) {
 			int i;
 			ma->options = a->options;
-			if (find_prpl(a->protocol))
+			if (gaim_find_prpl(a->protocol))
 				ma->protocol = a->protocol;
 			else if (protocols)
-				ma->protocol = ((struct prpl *)protocols->data)->protocol;
+				ma->protocol = GAIM_PLUGIN_PROTOCOL_INFO(
+						((GaimPlugin *)protocols->data))->protocol;
 			else
 				ma->protocol = -1;
 			g_snprintf(ma->iconfile, sizeof(ma->iconfile), "%s", a->iconfile);
@@ -1170,10 +1185,11 @@
 						a->proto_opt[i]);
 		} else {
 			ma->options = OPT_ACCT_REM_PASS;
-			if (find_prpl(DEFAULT_PROTO))
-				ma->protocol = DEFAULT_PROTO;
+			if (gaim_find_prpl(GAIM_PROTO_DEFAULT))
+				ma->protocol = GAIM_PROTO_DEFAULT;
 			else if (protocols)
-				ma->protocol = ((struct prpl *)protocols->data)->protocol;
+				ma->protocol = GAIM_PLUGIN_PROTOCOL_INFO(
+						((GaimPlugin *)protocols->data))->protocol;
 			else
 				ma->protocol = -1;
 		}
@@ -1361,25 +1377,37 @@
 	GtkTreeIter iter;
 
 	struct gaim_account *account = NULL;
-	struct prpl *p = NULL;
+	GaimPlugin *p = NULL;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 
 	gtk_tree_model_get_iter_from_string(model, &iter, path_str);
 	gtk_tree_model_get(model, &iter, COLUMN_DATA, &account, -1);
 
-	p = find_prpl(account->protocol);
-	if (!account->gc && p && p->login) {
-		struct prpl *p = find_prpl(account->protocol);
-		if (p && !(p->options & OPT_PROTO_NO_PASSWORD) &&
-			!(p->options & OPT_PROTO_PASSWORD_OPTIONAL) && !account->password[0]) {
+	p = gaim_find_prpl(account->protocol);
+
+	if (p != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
+
+	if (!account->gc && p && prpl_info->login) {
+		GaimPlugin *p = gaim_find_prpl(account->protocol);
+
+		if (p != NULL)
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
+
+		if (p && !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
+			!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL) &&
+			!account->password[0]) {
+
 			do_pass_dlg(account);
-		} else {
+		}
+		else {
 			serv_login(account);
 		}
 	} else if (account->gc) {
 		account->gc->wants_to_die = TRUE;
 		signoff(account->gc);
 	} else {
-		if (account->protocol == PROTO_TOC)
+		if (account->protocol == GAIM_PROTO_TOC)
 			do_error_dialog(_("TOC not found."), 
 					_("You have attempted to login an IM account using the "
 					 "TOC protocol.  Because this protocol is inferior to "
@@ -1693,7 +1721,7 @@
 	connecting_count--;
 	debug_printf("connecting_count: %d\n", connecting_count);
 
-	plugin_event(event_signon, gc);
+	gaim_event_broadcast(event_signon, gc);
 	system_log(log_signon, gc, NULL, OPT_LOG_BUDDY_SIGNON | OPT_LOG_MY_SIGNON);
 
 	/* away option given? */
@@ -1737,7 +1765,7 @@
 	if (get_iter_from_data(GTK_TREE_VIEW(treeview), gc->account, &iter)) {
 		gtk_list_store_set(model, &iter,
 						   COLUMN_ONLINE, TRUE,
-						   COLUMN_PROTOCOL, gc->prpl->name,
+						   COLUMN_PROTOCOL, gc->prpl->info->name,
 						   -1);
 	}
 
@@ -1980,7 +2008,7 @@
 {
 	gchar *buf;
 
-	plugin_event(event_error, gc, why);
+	gaim_event_broadcast(event_error, gc, why);
 	buf = g_strdup_printf(_("%s was unable to sign on"), gc->username);
 	hide_login_progress_common(gc, why, _("Signon Error"), buf);
 	g_free(buf);
@@ -2004,7 +2032,7 @@
 {
 	char buf[2048];
 
-	plugin_event(event_error, gc, why);
+	gaim_event_broadcast(event_error, gc, why);
 	g_snprintf(buf, sizeof(buf), _("%s has been signed off"), gc->username);
 	hide_login_progress_common(gc, why, _("Connection Error"), buf);
 }
@@ -2026,19 +2054,13 @@
 {
 	GList *wins;
 
-	/* UI stuff */
-	/* CONV XXX
-	convo_menu_remove(gc);
-	remove_icon_data(gc);
-       	*/
-
 	gaim_blist_remove_account(gc->account);
 
 	/* core stuff */
 	/* remove this here so plugins get a sensible count of connections */
 	connections = g_slist_remove(connections, gc);
 	debug_printf("date: %s\n", full_date());
-	plugin_event(event_signoff, gc);
+	gaim_event_broadcast(event_signoff, gc);
 	system_log(log_signoff, gc, NULL, OPT_LOG_BUDDY_SIGNON | OPT_LOG_MY_SIGNON);
 	/* set this in case the plugin died before really connecting.
 	   do it after calling the plugins so they can determine if
--- a/src/multi.h	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/multi.h	Fri Apr 25 06:47:33 2003 +0000
@@ -23,6 +23,7 @@
 #define _MULTI_H_
 
 #include "core.h"
+#include "plugin.h"
 
 /* ok. now the fun begins. first we create a connection structure */
 struct gaim_connection {
@@ -31,7 +32,7 @@
 	/* we need to do either oscar or TOC */
 	/* we make this as an int in case if we want to add more protocols later */
 	int protocol;
-	struct prpl *prpl;
+	GaimPlugin *prpl;
 	guint32 flags;
 
 	/* erg. */
--- a/src/perl.c	Thu Apr 24 03:52:25 2003 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1244 +0,0 @@
-/*
- * 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
- *
- * This was taken almost exactly from X-Chat. The power of the GPL.
- * Translated from X-Chat to Gaim by Eric Warmenhoven.
- * Originally by Erik Scrafford <eriks@chilisoft.com>.
- * X-Chat Copyright (C) 1998 Peter Zelezny.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#ifdef DEBUG
-#undef DEBUG
-#endif
-#endif
-#undef PACKAGE
-
-#ifdef USE_PERL
-
-#define group perl_group
-#ifdef _WIN32
-/* This took me an age to figure out.. without this __declspec(dllimport)
- * will be ignored.
- */
-#define HASATTRIBUTE
-#endif
-#include <EXTERN.h>
-#ifndef _SEM_SEMUN_UNDEFINED
-#define HAS_UNION_SEMUN
-#endif
-#include <perl.h>
-#include <XSUB.h>
-#ifndef _WIN32
-#include <sys/mman.h>
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#undef PACKAGE
-#include <stdio.h>
-#ifndef _WIN32
-#include <dirent.h>
-#else
-/* We're using perl's win32 port of this */
-#define dirent direct
-#endif
-#include <string.h>
-
-#undef group
-
-/* perl module support */
-#ifdef OLD_PERL
-extern void boot_DynaLoader _((CV * cv));
-#else
-extern void boot_DynaLoader _((pTHX_ CV * cv)); /* perl is so wacky */
-#endif
-
-#undef _
-#ifdef DEBUG
-#undef DEBUG
-#endif
-#ifdef _WIN32
-#undef pipe
-#endif
-#include "gaim.h"
-#include "prpl.h"
-#include "sound.h"
-
-#ifndef call_pv
-# define call_pv(i,j) perl_call_pv(i,j)
-#endif
-
-struct perlscript {
-	char *name;
-	char *version;
-	char *shutdowncallback; /* bleh */
-	struct gaim_plugin *plug;
-};
-
-struct _perl_event_handlers {
-	char *event_type;
-	char *handler_name;
-	struct gaim_plugin *plug;
-};
-
-struct _perl_timeout_handlers {
-	char *handler_name;
-	char *handler_args;
-	gint iotag;
-	struct gaim_plugin *plug;
-};
-
-static GList *perl_list = NULL; /* should probably extern this at some point */
-static GList *perl_timeout_handlers = NULL;
-static GList *perl_event_handlers = NULL;
-static PerlInterpreter *my_perl = NULL;
-static void perl_init();
-
-/* dealing with gaim */
-XS(XS_GAIM_register); /* set up hooks for script */
-XS(XS_GAIM_get_info); /* version, last to attempt signon, protocol */
-XS(XS_GAIM_print); /* lemme figure this one out... */
-XS(XS_GAIM_write_to_conv); /* write into conversation window */
-
-/* list stuff */
-XS(XS_GAIM_buddy_list); /* all buddies */
-XS(XS_GAIM_online_list); /* online buddies */
-
-/* server stuff */
-XS(XS_GAIM_command); /* send command to server */
-XS(XS_GAIM_user_info); /* given name, return struct buddy members */
-XS(XS_GAIM_print_to_conv); /* send message to someone */
-XS(XS_GAIM_print_to_chat); /* send message to chat room */
-XS(XS_GAIM_serv_send_im); /* send message to someone (but do not display) */
-
-/* handler commands */
-XS(XS_GAIM_add_event_handler); /* when servers talk */
-XS(XS_GAIM_remove_event_handler); /* remove a handler */
-XS(XS_GAIM_add_timeout_handler); /* figure it out */
-
-/* play sound */
-XS(XS_GAIM_play_sound); /*play a sound */
-
-static void
-#ifdef OLD_PERL
-xs_init()
-#else
-xs_init(pTHX)
-#endif
-{
-	char *file = __FILE__;
-
-	/* This one allows dynamic loading of perl modules in perl
-           scripts by the 'use perlmod;' construction*/
-	newXS ("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
-
-	/* load up all the custom Gaim perl functions */
-		newXS ("GAIM::register", XS_GAIM_register, "GAIM");
-	newXS ("GAIM::get_info", XS_GAIM_get_info, "GAIM");
-	newXS ("GAIM::print", XS_GAIM_print, "GAIM");
-	newXS ("GAIM::write_to_conv", XS_GAIM_write_to_conv, "GAIM");
-
-	newXS ("GAIM::buddy_list", XS_GAIM_buddy_list, "GAIM");
-	newXS ("GAIM::online_list", XS_GAIM_online_list, "GAIM");
-
-	newXS ("GAIM::command", XS_GAIM_command, "GAIM");
-	newXS ("GAIM::user_info", XS_GAIM_user_info, "GAIM");
-	newXS ("GAIM::print_to_conv", XS_GAIM_print_to_conv, "GAIM");
-	newXS ("GAIM::print_to_chat", XS_GAIM_print_to_chat, "GAIM");
-	newXS ("GAIM::serv_send_im", XS_GAIM_serv_send_im, "GAIM");
-
-	newXS ("GAIM::add_event_handler", XS_GAIM_add_event_handler, "GAIM");
-	newXS ("GAIM::remove_event_handler", XS_GAIM_remove_event_handler, "GAIM");
-	newXS ("GAIM::add_timeout_handler", XS_GAIM_add_timeout_handler, "GAIM");
-
-	newXS ("GAIM::play_sound", XS_GAIM_play_sound, "GAIM");
-}
-
-static char *escape_quotes(char *buf)
-{
-	static char *tmp_buf = NULL;
-	char *i, *j;
-
-	if (tmp_buf)
-		g_free(tmp_buf);
-	tmp_buf = g_malloc(strlen(buf) * 2 + 1);
-	for (i = buf, j = tmp_buf; *i; i++, j++) {
-		if (*i == '\'' || *i == '\\')
-			*j++ = '\\';
-		*j = *i;
-	}
-	*j = '\0';
-
-	return (tmp_buf);
-}
-
-/*
-  2003/02/06: execute_perl modified by Mark Doliner <mark@kingant.net>
-		Pass parameters by pushing them onto the stack rather than 
-		passing an array of strings.  This way, perl scripts can 
-		modify the parameters and we can get the changed values 
-		and then shoot ourselves.  I mean, uh, use them.
-
-  2001/06/14: execute_perl replaced by Martin Persson <mep@passagen.se>
-		previous use of perl_eval leaked memory, replaced with
-		a version that uses perl_call instead
-
-  30/11/2002: execute_perl modified by Eric Timme <timothy@voidnet.com>
-		args changed to char** so that we can have preparsed
-  		arguments again, and many headaches ensued! This essentially 
-		means we replaced one hacked method with a messier hacked 
-		method out of perceived necessity. Formerly execute_perl 
-		required a single char_ptr, and it would insert it into an 
-		array of character pointers and NULL terminate the new array.
-		Now we have to pass in pre-terminated character pointer arrays
-		to accomodate functions that want to pass in multiple arguments.
-
-		Previously arguments were preparsed because an argument list
-		was constructed in the form 'arg one','arg two' and was
-		executed via a call like &funcname(arglist) (see .59.x), so
-		the arglist was magically pre-parsed because of the method. 
-		With Martin Persson's change to perl_call we now need to
-		use a null terminated list of character pointers for arguments
-		if we wish them to be parsed. Lacking a better way to allow
-		for both single arguments and many I created a NULL terminated
-		array in every function that called execute_perl and passed
-		that list into the function.  In the former version a single
-		character pointer was passed in, and was placed into an array
-		of character pointers with two elements, with a NULL element
-		tacked onto the back, but this method no longer seemed prudent.
-
-		Enhancements in the future might be to get rid of pre-declaring
-		the array sizes?  I am not comfortable enough with this
-		subject to attempt it myself and hope it to stand the test
-		of time.
-*/
-
-static int 
-execute_perl(char *function, char **args)
-{
-	int count, i, ret_value = 1;
-	SV *sv_args[5];
-	STRLEN na;
-
-	/*
-	 * Set up the perl environment, push arguments onto the 
-	 * perl stack, then call the given function
-	 */
-	dSP;
-	ENTER;
-	SAVETMPS;
-	PUSHMARK(sp);
-	for (i=0; i<5; i++)
-		if (args[i]) {
-			sv_args[i] = sv_2mortal(newSVpv(args[i], 0));
-			XPUSHs(sv_args[i]);
-		}
-	PUTBACK;
-	count = call_pv(function, G_EVAL | G_SCALAR);
-	SPAGAIN;
-
-	/* Check for "die," make sure we have 1 argument, and set our return value */
-	if (SvTRUE(ERRSV)) {
-		debug_printf("Perl function %s exited abnormally: %s\n", function, SvPV(ERRSV, na));
-		POPs;
-	} else if (count != 1) {
-		/* This should NEVER happen.  G_SCALAR ensures that we WILL have 1 parameter */
-		debug_printf("Perl error from %s: expected 1 return value, but got %d\n", function, count);
-	} else {
-		ret_value = POPi;
-	}
-
-	/* Check for changed arguments */
-	for (i=0; i<5; i++)
-		if (args[i] && strcmp(args[i], SvPVX(sv_args[i]))) {
-			/*
-			 * Shizzel.  So the perl script changed one of the parameters, and we want 
-			 * this change to affect the original parameters.  args[i] is just a tempory 
-			 * little list of pointers.  We don't want to free args[i] here because the 
-			 * new parameter doesn't overwrite the data that args[i] points to.  That is 
-			 * done by the function that called execute_perl.  I'm not explaining this 
-			 * very well.  See, it's aggregate...  Oh, but if 2 perl scripts both modify 
-			 * the data, _that's_ a memleak.  This is really kind of hackish.  I should 
-			 * fix it.  Look how long this comment is.  Holy crap.
-			 */
-			args[i] = g_strdup(SvPV(sv_args[i], na));
-		}
-
-	PUTBACK;
-	FREETMPS;
-	LEAVE;
-
-	return ret_value;
-}
-
-void perl_unload_file(struct gaim_plugin *plug) {
-	char *atmp[1] = { "" };
-	struct perlscript *scp = NULL;
-	struct _perl_timeout_handlers *thn;
-	struct _perl_event_handlers *ehn;
-
-	GList *pl = perl_list;
-
-	debug_printf("Unloading %s\n", plug->path);
-	while (pl) {
-		scp = pl->data;
-		if (scp->plug == plug) {
-			perl_list = g_list_remove(perl_list, scp);
-			if (scp->shutdowncallback[0])
-				execute_perl(scp->shutdowncallback, atmp);
-			g_free(scp->name);
-			g_free(scp->version);
-			g_free(scp->shutdowncallback);
-			g_free(scp);	
-			break;
-		}
-		pl = pl->next;
-	}
-
-	pl = perl_timeout_handlers;
-	while (pl) {
-		thn = pl->data;
-		if (thn && thn->plug == plug) {
-			perl_timeout_handlers = g_list_remove(perl_timeout_handlers, thn);
-			g_source_remove(thn->iotag);
-			g_free(thn->handler_args);
-			g_free(thn->handler_name);
-			g_free(thn);
-		}
-		pl = pl->next;
-	}
-
-	pl = perl_event_handlers;
-	while (pl) {
-		ehn = pl->data;
-		if (ehn && ehn->plug == plug) {
-			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
-			g_free(ehn->event_type);
-			g_free(ehn->handler_name);
-			g_free(ehn);
-		}
-		pl = pl->next;
-	}
-
-	plug->handle = NULL;
-	plugins = g_list_remove(plugins, plug);
-	save_prefs();
-}
-
-int perl_load_file(char *script_name)
-{
-	char *atmp[1] = { script_name };
-	struct gaim_plugin *plug = NULL;
-	GList *p = probed_plugins;
-	GList *s;
-	struct perlscript *scp;
-	int ret;
-
-	if (my_perl == NULL)
-		perl_init();
-	
-	while (p) {
-		plug = (struct gaim_plugin *)p->data;
-		if (!strcmp(script_name, plug->path)) 
-			break;
-		p = p->next;
-	}
-	
-	if (!plug) {
-		probe_perl(script_name);
-	}
-
-	plug->handle = plug->path;
-	plugins = g_list_append(plugins, plug);
-
-	ret = execute_perl("load_n_eval", atmp);
-
-	s = perl_list;
-	while (s) {
-		scp = s->data;
-	
-		if (!strcmp(scp->name, plug->desc.name) &&
-		    !strcmp(scp->version, plug->desc.version))
-			break;
-		s = s->next;
-	}
-
-	if (!s) {
-		g_snprintf(plug->error, sizeof(plug->error), _("GAIM::register not called with proper arguments.  Consult PERL-HOWTO."));
-		return 0;
-	}
-	
-	plug->error[0] = '\0';
-	return ret;
-}
-
-struct gaim_plugin *probe_perl(char *filename) {
-
-	/* XXX This woulld be much faster if I didn't create a new
-	 *     PerlInterpreter every time I probed a plugin */
-
-	PerlInterpreter *prober = perl_alloc();
-	struct gaim_plugin * plug = NULL;
-	char *argv[] = {"", filename};
-	int count;
-	perl_construct(prober);
-	perl_parse(prober, NULL, 2, argv, NULL);
-	
-	{
-		dSP;
-		ENTER;
-		SAVETMPS;
-		PUSHMARK(SP);
-
-		count = perl_call_pv("description", G_NOARGS | G_ARRAY | G_EVAL);
-		SPAGAIN;
-		if (count == (sizeof(struct gaim_plugin_description) - sizeof(int)) / sizeof(char*)) {
-			plug = g_new0(struct gaim_plugin, 1);
-			plug->type = perl_script;
-			g_snprintf(plug->path, sizeof(plug->path), filename);
-			plug->desc.iconfile = g_strdup(POPp);
-			plug->desc.url = g_strdup(POPp);
-			plug->desc.authors = g_strdup(POPp);
-			plug->desc.description = g_strdup(POPp);
-			plug->desc.version = g_strdup(POPp);
-			plug->desc.name = g_strdup(POPp);
-		}
-			
-		PUTBACK;
-		FREETMPS;
-		LEAVE;
-	}
-	perl_destruct(prober);
-	perl_free(prober);
-	return plug;
-}
-
-static void perl_init()
-{	/* changed the name of the variable from load_file to
-	   perl_definitions since now it does much more than defining
-	   the load_file sub. Moreover, deplaced the initialisation to
-	   the xs_init function. (TheHobbit)*/
-	char *perl_args[] = { "", "-e", "0", "-w" };
-	char perl_definitions[] =
-	{
-		/* We use to function one to load a file the other to
-		   execute the string obtained from the first and holding
-		   the file conents. This allows to have a realy local $/
-		   without introducing temp variables to hold the old
-		   value. Just a question of style:) */ 
-		"sub load_file{"
-		  "my $f_name=shift;"
-		  "local $/=undef;"
-		  "open FH,$f_name or return \"__FAILED__\";"
-		  "$_=<FH>;"
-		  "close FH;"
-		  "return $_;"
-		"}"
-		"sub load_n_eval{"
-		  "my $f_name=shift;"
-		  "my $strin=load_file($f_name);"
-		  "return 2 if($strin eq \"__FAILED__\");"
-		  "eval $strin;"
-		  "if($@){"
-		    /*"  #something went wrong\n"*/
-		    "GAIM::print(\"Errors loading file $f_name:\\n\",\"$@\");"
-		    "return 1;"
-		  "}"
-		  "return 0;"
-		"}"
-	};
-
-	my_perl = perl_alloc();
-	perl_construct(my_perl);
-#ifdef DEBUG
-	perl_parse(my_perl, xs_init, 4, perl_args, NULL);
-#else
-	perl_parse(my_perl, xs_init, 3, perl_args, NULL);
-#endif
- #ifdef HAVE_PERL_EVAL_PV
-	eval_pv(perl_definitions, TRUE);
-#else
-	perl_eval_pv(perl_definitions, TRUE); /* deprecated */
-#endif
-
-
-}
-
-void perl_end()
-{
-	char *atmp[1] = { "" };
-	struct perlscript *scp;
-	struct _perl_timeout_handlers *thn;
-	struct _perl_event_handlers *ehn;
-
-	while (perl_list) {
-		scp = perl_list->data;
-		perl_list = g_list_remove(perl_list, scp);
-		if (scp->shutdowncallback[0])
-			execute_perl(scp->shutdowncallback, atmp);
-		g_free(scp->name);
-		g_free(scp->version);
-		g_free(scp->shutdowncallback);
-		g_free(scp);
-	}
-
-	while (perl_timeout_handlers) {
-		thn = perl_timeout_handlers->data;
-		perl_timeout_handlers = g_list_remove(perl_timeout_handlers, thn);
-		g_source_remove(thn->iotag);
-		g_free(thn->handler_args);
-		g_free(thn->handler_name);
-		g_free(thn);
-	}
-
-	while (perl_event_handlers) {
-		ehn = perl_event_handlers->data;
-		perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
-		g_free(ehn->event_type);
-		debug_printf("handler_name: %s\n", ehn->handler_name);
-		g_free(ehn->handler_name);
-		g_free(ehn);
-	}
-
-	if (my_perl != NULL) {
-		perl_destruct(my_perl);
-		perl_free(my_perl);
-		my_perl = NULL;
-	}
-}
-
-XS (XS_GAIM_register)
-{
-	char *name, *ver, *callback, *unused; /* exactly like X-Chat, eh? :) */
-	unsigned int junk;
-	struct perlscript *scp;
-	struct gaim_plugin *plug = NULL;
-	GList *pl = plugins;
-
-	dXSARGS;
-	items = 0;
-
-	name = SvPV (ST (0), junk);
-	ver = SvPV (ST (1), junk);
-	callback = SvPV (ST (2), junk);
-	unused = SvPV (ST (3), junk);
-
-	while (pl) {
-		plug = pl->data;
-
-		if (!strcmp(name, plug->desc.name) &&
-		    !strcmp(ver, plug->desc.version)) {
-			break;
-		}
-		pl = pl->next;
-	}
-
-	if (plug) {
-		scp = g_new0(struct perlscript, 1);
-		scp->name = g_strdup(name);
-		scp->version = g_strdup(ver);
-		scp->shutdowncallback = g_strdup(callback);
-		scp->plug = plug; 
-		perl_list = g_list_append(perl_list, scp);
-	}
-	XST_mPV (0, plug->path);
-	XSRETURN (1);
-}
-
-XS (XS_GAIM_get_info)
-{
-	int i = 0;
-	dXSARGS;
-	items = 0;
-
-	switch(SvIV(ST(0))) {
-	case 0:
-		XST_mPV(0, VERSION);
-		i = 1;
-		break;
-	case 1:
-		{
-			GSList *c = connections;
-			struct gaim_connection *gc;
-
-			while (c) {
-				gc = (struct gaim_connection *)c->data;
-				XST_mIV(i++, (guint)gc);
-				c = c->next;
-			}
-		}
-		break;
-	case 2:
-		{
-			struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
-			if (g_slist_find(connections, gc))
-				XST_mIV(i++, gc->protocol);
-			else
-				XST_mIV(i++, -1);
-		}
-		break;
-	case 3:
-		{
-			struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
-			if (g_slist_find(connections, gc))
-				XST_mPV(i++, gc->username);
-			else
-				XST_mPV(i++, "");
-		}
-		break;
-	case 4:
-		{
-			struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
-			if (g_slist_find(connections, gc))
-				XST_mIV(i++, g_slist_index(gaim_accounts, gc->account));
-			else
-				XST_mIV(i++, -1);
-		}
-		break;
-	case 5:
-		{
-			GSList *a = gaim_accounts;
-			while (a) {
-				struct gaim_account *account = a->data;
-				XST_mPV(i++, account->username);
-				a = a->next;
-			}
-		}
-		break;
-	case 6:
-		{
-			GSList *a = gaim_accounts;
-			while (a) {
-				struct gaim_account *account = a->data;
-				XST_mIV(i++, account->protocol);
-				a = a->next;
-			}
-		}
-		break;
-	case 7:
-		{
-			struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
-			if (g_slist_find(connections, gc))
-				XST_mPV(i++, gc->prpl->name);
-			else
-				XST_mPV(i++, "Unknown");
-		}
-		break;
-	default:
-		XST_mPV(0, "Error2");
-		i = 1;
-	}
-
-	XSRETURN(i);
-}
-
-XS (XS_GAIM_print)
-{
-	char *title;
-	char *message;
-	unsigned int junk;
-	dXSARGS;
-	items = 0;
-
-	title = SvPV(ST(0), junk);
-	message = SvPV(ST(1), junk);
-	do_error_dialog(title, message, GAIM_INFO);
-	XSRETURN(0);
-}
-
-XS (XS_GAIM_buddy_list)
-{
-	struct gaim_connection *gc;
-	struct buddy *buddy;
-	struct group *g;
-	GaimBlistNode *gnode,*bnode;
-	int i = 0;
-	dXSARGS;
-	items = 0;
-
-	gc = (struct gaim_connection *)SvIV(ST(0));
-
-	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-		g = (struct group *)gnode;
-		for(bnode = gnode->child; bnode; bnode = bnode->next) {
-			if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-				continue;
-			buddy = (struct buddy *)bnode;
-			if(buddy->account == gc->account)
-				XST_mPV(i++, buddy->name);
-		}
-	}
-	XSRETURN(i);
-}
-
-XS (XS_GAIM_online_list)
-{
-	struct gaim_connection *gc;
-	struct buddy *b;
-	struct group *g;
-	GaimBlistNode *gnode,*bnode;
-	int i = 0;
-	dXSARGS;
-	items = 0;
-
-	gc = (struct gaim_connection *)SvIV(ST(0));
-
-	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-		g = (struct group *)gnode;
-		for(bnode = gnode->child; bnode; bnode = bnode->next) {
-			if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-				continue;
-			b = (struct buddy *)bnode;
-			if (b->account == gc->account && GAIM_BUDDY_IS_ONLINE(b)) XST_mPV(i++, b->name);
-		}
-	}
-	XSRETURN(i);
-}
-
-XS (XS_GAIM_command)
-{
-	unsigned int junk;
-	char *command = NULL;
-	dXSARGS;
-	items = 0;
-
-	command = SvPV(ST(0), junk);
-	if (!command) XSRETURN(0);
-	if (!strncasecmp(command, "signon", 6)) {
-		int index = SvIV(ST(1));
-		if (g_slist_nth_data(gaim_accounts, index))
-		serv_login(g_slist_nth_data(gaim_accounts, index));
-	} else if (!strncasecmp(command, "signoff", 7)) {
-		struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
-		if (g_slist_find(connections, gc)) signoff(gc);
-		else signoff_all(NULL, NULL);
-	} else if (!strncasecmp(command, "info", 4)) {
-		struct gaim_connection *gc = (struct gaim_connection *)SvIV(ST(1));
-		if (g_slist_find(connections, gc))
-			serv_set_info(gc, SvPV(ST(2), junk));
-	} else if (!strncasecmp(command, "away", 4)) {
-		char *message = SvPV(ST(1), junk);
-		static struct away_message a;
-		g_snprintf(a.message, sizeof(a.message), "%s", message);
-		do_away_message(NULL, &a);
-	} else if (!strncasecmp(command, "back", 4)) {
-		do_im_back();
-	} else if (!strncasecmp(command, "idle", 4)) {
-		GSList *c = connections;
-		struct gaim_connection *gc;
-
-		while (c) {
-			gc = (struct gaim_connection *)c->data;
-			serv_set_idle(gc, SvIV(ST(1)));
-			c = c->next;
-		}
-	} else if (!strncasecmp(command, "warn", 4)) {
-		GSList *c = connections;
-		struct gaim_connection *gc;
-
-		while (c) {
-			gc = (struct gaim_connection *)c->data;
-			serv_warn(gc, SvPV(ST(1), junk), SvIV(ST(2)));
-			c = c->next;
-		}
-	}
-
-	XSRETURN(0);
-}
-
-XS (XS_GAIM_user_info)
-{
-	struct gaim_connection *gc;
-	unsigned int junk;
-	struct buddy *buddy = NULL;
-	dXSARGS;
-	items = 0;
-
-	gc = (struct gaim_connection *)SvIV(ST(0));
-	if (g_slist_find(connections, gc))
-		buddy = gaim_find_buddy(gc->account, SvPV(ST(1), junk));
-
-	if (!buddy)
-		XSRETURN(0);
-	XST_mPV(0, buddy->name);
-	XST_mPV(1, gaim_get_buddy_alias(buddy));
-	XST_mPV(2, GAIM_BUDDY_IS_ONLINE(buddy) ? "Online" : "Offline");
-	XST_mIV(3, buddy->evil);
-	XST_mIV(4, buddy->signon);
-	XST_mIV(5, buddy->idle);
-	XSRETURN(6);
-}
-
-XS (XS_GAIM_write_to_conv)
-{
-	char *nick, *who, *what;
-	struct gaim_conversation *c;
-	int junk;
-	int send, wflags;
-	dXSARGS;
-	items = 0;
-
-	nick = SvPV(ST(0), junk);
-	send = SvIV(ST(1));
-	what = SvPV(ST(2), junk);
-	who = SvPV(ST(3), junk);
-	
-	if (!*who) who=NULL;
-	
-	switch (send) {
-		case 0: wflags=WFLAG_SEND; break;
-		case 1: wflags=WFLAG_RECV; break;
-		case 2: wflags=WFLAG_SYSTEM; break;
-		default: wflags=WFLAG_RECV;
-	}	
-
-	c = gaim_find_conversation(nick);
-
-	if (!c)
-		c = gaim_conversation_new(GAIM_CONV_IM, NULL, nick);
-
-	gaim_conversation_write(c, who, what, -1, wflags, time(NULL));
-	XSRETURN(0);
-}
-
-XS (XS_GAIM_serv_send_im)
-{
-	struct gaim_connection *gc;
-	char *nick, *what;
-	int isauto;
-	int junk;
-	dXSARGS;
-	items = 0;
-
-	gc = (struct gaim_connection *)SvIV(ST(0));
-	nick = SvPV(ST(1), junk);
-	what = SvPV(ST(2), junk);
-	isauto = SvIV(ST(3));
-
-	if (!g_slist_find(connections, gc)) {
-		XSRETURN(0);
-		return;
-	}
-	serv_send_im(gc, nick, what, -1, isauto);
-	XSRETURN(0);
-}
-
-XS (XS_GAIM_print_to_conv)
-{
-	struct gaim_connection *gc;
-	char *nick, *what;
-	int isauto;
-	struct gaim_conversation *c;
-	unsigned int junk;
-	dXSARGS;
-	items = 0;
-
-	gc = (struct gaim_connection *)SvIV(ST(0));
-	nick = SvPV(ST(1), junk);
-	what = SvPV(ST(2), junk);
-	isauto = SvIV(ST(3));
-	if (!g_slist_find(connections, gc)) {
-		XSRETURN(0);
-		return;
-	}
-
-	c = gaim_find_conversation(nick);
-
-	if (!c)
-		c = gaim_conversation_new(GAIM_CONV_IM, gc->account, nick);
-	else
-		gaim_conversation_set_account(c, gc->account);
-
-	gaim_conversation_write(c, NULL, what, -1, 
-				(WFLAG_SEND | (isauto ? WFLAG_AUTO : 0)), time(NULL));
-	serv_send_im(gc, nick, what, -1, isauto ? IM_FLAG_AWAY : 0);
-	XSRETURN(0);
-}
-
-
-	
-XS (XS_GAIM_print_to_chat)
-{
-	struct gaim_connection *gc;
-	int id;
-	char *what;
-	struct gaim_conversation *b = NULL;
-	GSList *bcs;
-	unsigned int junk;
-	dXSARGS;
-	items = 0;
-
-	gc = (struct gaim_connection *)SvIV(ST(0));
-	id = SvIV(ST(1));
-	what = SvPV(ST(2), junk);
-
-	if (!g_slist_find(connections, gc)) {
-		XSRETURN(0);
-		return;
-	}
-	bcs = gc->buddy_chats;
-	while (bcs) {
-		b = (struct gaim_conversation *)bcs->data;
-
-		if (gaim_chat_get_id(gaim_conversation_get_chat_data(b)) == id)
-			break;
-		bcs = bcs->next;
-		b = NULL;
-	}
-	if (b)
-		serv_chat_send(gc, id, what);
-	XSRETURN(0);
-}
-
-int perl_event(enum gaim_event event, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
-{
-	char *buf[5] = { NULL, NULL, NULL, NULL, NULL }; /* Maximum of 5 args */
-	char tmpbuf1[16], tmpbuf2[16], tmpbuf3[1];
-	GList *handler;
-	struct _perl_event_handlers *data;
-	int handler_return;
-
-	tmpbuf1[0] = '\0';
-	tmpbuf2[0] = '\0';
-	tmpbuf3[0] = '\0';
-
-	/* Make a pretty array of char*'s with which to call perl functions */
-	switch (event) {
-	case event_signon:
-	case event_signoff:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		break;
-	case event_away:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = ((struct gaim_connection *)arg1)->away ?
-				((struct gaim_connection *)arg1)->away : tmpbuf2;
-		break;
-	case event_im_recv:
-		if (!*(char**)arg2 || !*(char**)arg3) return 1;
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = *(char **)arg2;
-		buf[2] = *(char **)arg3;
-		break;
-	case event_im_send:
-		if (!*(char**)arg3) return 1;
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = arg2 ? arg2 : tmpbuf3;
-		buf[2] = *(char **)arg3;
-		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:
-	case event_got_typing:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = arg2;
-		break;
-	case event_chat_invited:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = arg2;
-		buf[2] = arg3;
-		buf[3] = arg4;
-		break;
-	case event_chat_join:
-	case event_chat_buddy_join:
-	case event_chat_buddy_leave:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
-		buf[1] = tmpbuf2;
-		buf[2] = arg3;
-		break;
-	case event_chat_leave:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
-		buf[1] = tmpbuf2;
-		break;
-	case event_chat_recv:
-		if (!*(char**)arg3 || !*(char**)arg4) return 1;
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
-		buf[1] = tmpbuf2;
-		buf[2] = *(char **)arg3;
-		buf[3] = *(char **)arg4;
-		break;
-	case event_chat_send_invite:
-		if (!*(char**)arg4) return 1;
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
-		buf[1] = tmpbuf2;
-		buf[2] = arg3;
-		buf[3] = *(char **)arg4;
-		break;
-	case event_chat_send:
-		if (!*(char**)arg3) return 1;
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		g_snprintf(tmpbuf2, 16, "%d", (int)arg2);
-		buf[1] = tmpbuf2;
-		buf[2] = *(char **)arg3;
-		break;
-	case event_warned:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = arg2 ? arg2 : tmpbuf3;
-		g_snprintf(tmpbuf2, 16, "%d", (int)arg3);
-		buf[2] = tmpbuf2;
-		break;
-	case event_quit:
-	case event_blist_update:
-		buf[0] = tmpbuf3;
-		break;
-	case event_new_conversation:
-	case event_del_conversation:
-		buf[0] = arg1;
-		break;
-	case event_im_displayed_sent:
-		if (!*(char**)arg3) return 1;
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = arg2;
-		buf[2] = *(char **)arg3;
-		break;
-	case event_im_displayed_rcvd:
-		g_snprintf(tmpbuf1, 16, "%lu", (unsigned long)arg1);
-		buf[0] = tmpbuf1;
-		buf[1] = arg2;
-		buf[2] = arg3 ? arg3 : tmpbuf3;
-		break;
-	case event_draw_menu:
-		/* we can't handle this usefully without gtk/perl bindings */
-		return 0;
-	default:
-		debug_printf("someone forgot to handle %s in the perl binding\n", event_name(event));
-		return 0;
-	}
-
-	/* Call any applicable functions */
-	for (handler = perl_event_handlers; handler != NULL; handler = handler->next) {
-		data = handler->data;
-		if (!strcmp(event_name(event), data->event_type)) {
-			handler_return = execute_perl(data->handler_name, buf);
-			if (handler_return) {
-				return handler_return;
-			}
-		}
-	}
-
-	/* Now make changes from perl scripts affect the real data */
-	switch (event) {
-	case event_im_recv:
-		if (buf[1] != *(char **)arg2) {
-			free(*(char **)arg2);
-			*(char **)arg2 = buf[1];
-		}
-		if (buf[2] != *(char **)arg3) {
-			free(*(char **)arg3);
-			*(char **)arg3 = buf[2];
-		}
-		break;
-	case event_im_send:
-		if (buf[2] != *(char **)arg3) {
-			free(*(char **)arg3);
-			*(char **)arg3 = buf[2];
-		}
-		break;
-	case event_chat_recv:
-		if (buf[2] != *(char **)arg3) {
-			free(*(char **)arg3);
-			*(char **)arg3 = buf[2];
-		}
-		if (buf[3] != *(char **)arg4) {
-			free(*(char **)arg4);
-			*(char **)arg4 = buf[3];
-		}
-		break;
-	case event_chat_send_invite:
-		if (buf[3] != *(char **)arg4) {
-			free(*(char **)arg4);
-			*(char **)arg4 = buf[3];
-		}
-		break;
-	case event_chat_send:
-		if (buf[2] != *(char **)arg3) {
-			free(*(char **)arg3);
-			*(char **)arg3 = buf[2];
-		}
-		break;
-	case event_im_displayed_sent:
-		if (buf[2] != *(char **)arg3) {
-			free(*(char **)arg3);
-			*(char **)arg3 = buf[2];
-		}
-		break;
-	default:
-		break;
-	}
-
-	return 0;
-}
-
-XS (XS_GAIM_add_event_handler)
-{
-	unsigned int junk;
-	struct _perl_event_handlers *handler;
-	char *handle;
-	struct gaim_plugin *plug;
-	GList *p = plugins;
-	dXSARGS;
-	items = 0;
-	
-	handle = SvPV(ST(0), junk);
-	while (p) {
-		plug = p->data;
-		if (!strcmp(handle, plug->path))
-			break;
-		p = p->next;
-	}
-
-	if (p) {
-		handler = g_new0(struct _perl_event_handlers, 1);
-		handler->event_type = g_strdup(SvPV(ST(1), junk));
-		handler->handler_name = g_strdup(SvPV(ST(2), junk));
-		handler->plug = plug;
-		perl_event_handlers = g_list_append(perl_event_handlers, handler);
-		debug_printf("registered perl event handler for %s\n", handler->event_type);
-	} else {
-		debug_printf("Invalid handle (%s) registering perl event handler\n", handle);
-	}
-	
-	XSRETURN_EMPTY;
-}
-
-XS (XS_GAIM_remove_event_handler)
-{
-	unsigned int junk;
-	struct _perl_event_handlers *ehn;
-	GList *cur = perl_event_handlers;
-	dXSARGS;
-	items = 0;
-
-	while (cur) {
-		GList *next = cur->next;
-		ehn = cur->data;
-
-		if (!strcmp(ehn->event_type, SvPV(ST(0), junk)) &&
-			!strcmp(ehn->handler_name, SvPV(ST(1), junk)))
-		{
-			perl_event_handlers = g_list_remove(perl_event_handlers, ehn);
-			g_free(ehn->event_type);
-			g_free(ehn->handler_name);
-			g_free(ehn);
-		}
-
-		cur = next;
-	}
-}
-
-static int perl_timeout(gpointer data)
-{
-	char *atmp[1] = { NULL };
-	struct _perl_timeout_handlers *handler = data;
-
-	atmp[0] = escape_quotes(handler->handler_args);
-	execute_perl(handler->handler_name, atmp);
-
-	perl_timeout_handlers = g_list_remove(perl_timeout_handlers, handler);
-	g_free(handler->handler_args);
-	g_free(handler->handler_name);
-	g_free(handler);
-
-	return 0; /* returning zero removes the timeout handler */
-}
-
-XS (XS_GAIM_add_timeout_handler)
-{
-	unsigned int junk;
-	long timeout;
-	struct _perl_timeout_handlers *handler;
-	char *handle;
-	struct gaim_plugin *plug;
-	GList *p = plugins;
-	
-	dXSARGS;
-	items = 0;
-	
-	handle = SvPV(ST(0), junk);
-	while (p) {
-		plug = p->data;
-		if (!strcmp(handle, plug->path))
-			break;
-		p = p->next;
-	}
-
-	if (p) {
-		handler = g_new0(struct _perl_timeout_handlers, 1);
-		timeout = 1000 * SvIV(ST(1));
-		debug_printf("Adding timeout for %ld seconds.\n", timeout/1000);
-		handler->plug = plug;
-		handler->handler_name = g_strdup(SvPV(ST(2), junk));
-		handler->handler_args = g_strdup(SvPV(ST(3), junk));
-		perl_timeout_handlers = g_list_append(perl_timeout_handlers, handler);
-		handler->iotag = g_timeout_add(timeout, perl_timeout, handler);
-	} else {
-		debug_printf("Invalid handle (%s) in adding perl timeout handler.", handle);
-	}
-	XSRETURN_EMPTY;
-}
-
-XS (XS_GAIM_play_sound)
-{
-	int id;
-	dXSARGS;
-
-	items = 0;
-
-	id = SvIV(ST(0));
-
-	gaim_sound_play_event(id);
-
-	XSRETURN_EMPTY;
-}
-
-extern void unload_perl_scripts()
-{
-	perl_end();
-	perl_init();
-}
-
-
-#endif /* USE_PERL */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugin.c	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,715 @@
+/*
+ * 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 plugin
+ *
+ * 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
+ *
+ * Yeah, well now I'm re-writing a good portion of it! The perl stuff was
+ * a hack. Tsk tsk! -- Christian <chipx86@gnupdate.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gaim.h"
+#include "prpl.h"
+#include "event.h"
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include "win32dep.h"
+#endif
+
+#ifdef _WIN32
+# define PLUGIN_EXT ".dll"
+#else
+# define PLUGIN_EXT ".so"
+#endif
+
+static GList *loaded_plugins = NULL;
+static GList *plugins = NULL;
+static GList *plugin_loaders = NULL;
+
+static size_t search_path_count = 0;
+static char **search_paths = NULL;
+
+static void (*probe_cb)(void *) = NULL;
+static void *probe_cb_data = NULL;
+static void (*load_cb)(GaimPlugin *, void *) = NULL;
+static void *load_cb_data = NULL;
+static void (*unload_cb)(GaimPlugin *, void *) = NULL;
+static void *unload_cb_data = NULL;
+
+#ifdef GAIM_PLUGINS
+static int
+is_so_file(const char *filename, const char *ext)
+{
+	int len, extlen;
+
+	if (filename == NULL || *filename == '\0' || ext == NULL)
+		return 0;
+
+	extlen = strlen(ext);
+	len = strlen(filename) - extlen;
+
+	if (len < 0)
+		return 0;
+
+	return (!strncmp(filename + len, ext, extlen));
+}
+
+static gboolean
+__loader_supports_file(GaimPlugin *loader, const char *filename)
+{
+	GList *l, *exts;
+	GaimPlugin *plugin;
+
+	for (l = plugin_loaders; l != NULL; l = l->next) {
+		plugin = l->data;
+
+		for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
+			 exts != NULL;
+			 exts = exts->next) {
+
+			if (is_so_file(filename, (char *)exts->data))
+				return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static GaimPlugin *
+__find_loader_for_plugin(const GaimPlugin *plugin)
+{
+	GaimPlugin *loader;
+	GList *l;
+
+	if (plugin->path == NULL)
+		return NULL;
+
+	for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) {
+		loader = l->data;
+
+		if (loader->info->type == GAIM_PLUGIN_LOADER &&
+			__loader_supports_file(loader, plugin->path)) {
+
+			return loader;
+		}
+
+		loader = NULL;
+	}
+
+	return NULL;
+}
+
+static gint
+compare_prpl(GaimPlugin *a, GaimPlugin *b)
+{
+	/* neg if a before b, 0 if equal, pos if a after b */
+	return ((GAIM_IS_PROTOCOL_PLUGIN(a)
+			 ? GAIM_PLUGIN_PROTOCOL_INFO(a)->protocol : -1) -
+			((GAIM_IS_PROTOCOL_PLUGIN(b)
+			 ? GAIM_PLUGIN_PROTOCOL_INFO(b)->protocol : -1)));
+}
+
+#endif /* GAIM_PLUGINS */
+
+GaimPlugin *
+gaim_plugin_new(gboolean native, const char *path)
+{
+	GaimPlugin *plugin;
+
+	plugin = g_new0(GaimPlugin, 1);
+
+	plugin->native_plugin = native;
+	plugin->path = (path == NULL ? NULL : g_strdup(path));
+
+	return plugin;
+}
+
+GaimPlugin *
+gaim_plugin_probe(const char *filename)
+{
+#ifdef GAIM_PLUGINS
+	GaimPlugin *plugin = NULL;
+	GaimPlugin *loader;
+	gboolean (*gaim_init_plugin)(GaimPlugin *);
+
+	g_return_val_if_fail(filename != NULL, NULL);
+
+	plugin = gaim_plugins_find_with_filename(filename);
+
+	if (plugin != NULL)
+		return plugin;
+
+	plugin = gaim_plugin_new(is_so_file(filename, PLUGIN_EXT), filename);
+
+	if (plugin->native_plugin) {
+		plugin->handle = g_module_open(filename, 0);
+
+		if (plugin->handle == NULL) {
+			plugin->error = g_strdup(g_module_error());
+
+			return plugin;
+		}
+
+		if (!g_module_symbol(plugin->handle, "gaim_init_plugin",
+							 (gpointer *)&gaim_init_plugin)) {
+			g_module_close(plugin->handle);
+			plugin->handle = NULL;
+
+			debug_printf("%s is unloadable %s\n",
+						 plugin->path, g_module_error());
+
+			gaim_plugin_destroy(plugin);
+
+			return NULL;
+		}
+	}
+	else {
+		loader = __find_loader_for_plugin(plugin);
+
+		if (loader == NULL) {
+			gaim_plugin_destroy(plugin);
+
+			return NULL;
+		}
+
+		gaim_init_plugin = GAIM_PLUGIN_LOADER_INFO(loader)->probe;
+	}
+
+	plugin->error = NULL;
+
+	if (!gaim_init_plugin(plugin) || plugin->info == NULL) {
+		char buf[BUFSIZ];
+
+		g_snprintf(buf, sizeof(buf),
+				   _("The plugin %s did not return any valid plugin "
+					 "information"),
+				   plugin->path);
+
+		do_error_dialog(_("Gaim was unable to load your plugin."), buf,
+						GAIM_ERROR);
+
+		gaim_plugin_destroy(plugin);
+
+		return NULL;
+	}
+
+	return plugin;
+#else
+	return NULL;
+#endif /* !GAIM_PLUGINS */
+}
+
+gboolean
+gaim_plugin_load(GaimPlugin *plugin)
+{
+#ifdef GAIM_PLUGINS
+	g_return_val_if_fail(plugin != NULL, FALSE);
+
+	if (gaim_plugin_is_loaded(plugin))
+		return TRUE;
+
+	if (plugin->native_plugin) {
+		if (plugin->info != NULL && plugin->info->load != NULL)
+			plugin->info->load(plugin);
+	}
+	else {
+		GaimPlugin *loader;
+		GaimPluginLoaderInfo *loader_info;
+
+		loader = __find_loader_for_plugin(plugin);
+
+		if (loader == NULL)
+			return FALSE;
+
+		loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
+
+		if (loader_info->load != NULL)
+			loader_info->load(plugin);
+	}
+
+	loaded_plugins = g_list_append(loaded_plugins, plugin);
+
+	plugin->loaded = TRUE;
+
+	/* TODO */
+	if (load_cb != NULL)
+		load_cb(plugin, load_cb_data);
+
+	return TRUE;
+
+#else
+	return FALSE;
+#endif /* !GAIM_PLUGINS */
+}
+
+gboolean
+gaim_plugin_unload(GaimPlugin *plugin)
+{
+#ifdef GAIM_PLUGINS
+	g_return_val_if_fail(plugin != NULL, FALSE);
+
+	loaded_plugins = g_list_remove(loaded_plugins, plugin);
+
+	g_return_val_if_fail(gaim_plugin_is_loaded(plugin), FALSE);
+
+	debug_printf("Unloading plugin %s\n", plugin->info->name);
+
+	/* cancel any pending dialogs the plugin has */
+	do_ask_cancel_by_handle(plugin);
+
+	plugin->loaded = FALSE;
+
+	if (plugin->native_plugin) {
+		if (plugin->info->unload != NULL)
+			plugin->info->unload(plugin);
+
+		if (plugin->info->type == GAIM_PLUGIN_PROTOCOL) {
+			GaimPluginProtocolInfo *prpl_info;
+			GList *l;
+			struct proto_user_split *pus;
+			struct proto_user_opt *puo;
+
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin);
+
+			for (l = prpl_info->user_splits; l != NULL; l = l->next) {
+				pus = l->data;
+
+				g_free(pus->label);
+				g_free(pus->def);
+				g_free(pus);
+			}
+
+			g_list_free(prpl_info->user_splits);
+
+			for (l = prpl_info->user_opts; l != NULL; l = l->next) {
+				puo = l->data;
+
+				g_free(puo->label);
+				g_free(puo->def);
+				g_free(puo);
+			}
+
+			g_list_free(prpl_info->user_opts);
+		}
+	}
+	else {
+		GaimPlugin *loader;
+		GaimPluginLoaderInfo *loader_info;
+
+		loader = __find_loader_for_plugin(plugin);
+
+		if (loader == NULL)
+			return FALSE;
+
+		loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
+
+		if (loader_info->load != NULL)
+			loader_info->unload(plugin);
+	}
+
+	gaim_signals_disconnect_by_handle(plugin);
+
+	/* TODO */
+	if (unload_cb != NULL)
+		unload_cb(plugin, unload_cb_data);
+
+	return TRUE;
+#endif /* GAIM_PLUGINS */
+}
+
+gboolean
+gaim_plugin_reload(GaimPlugin *plugin)
+{
+#ifdef GAIM_PLUGINS
+	g_return_val_if_fail(plugin != NULL, FALSE);
+	g_return_val_if_fail(gaim_plugin_is_loaded(plugin), FALSE);
+
+	if (!gaim_plugin_unload(plugin))
+		return FALSE;
+
+	if (!gaim_plugin_load(plugin))
+		return FALSE;
+
+	return TRUE;
+#else
+	return NULL;
+#endif /* !GAIM_PLUGINS */
+}
+
+void
+gaim_plugin_destroy(GaimPlugin *plugin)
+{
+	g_return_if_fail(plugin != NULL);
+
+	if (gaim_plugin_is_loaded(plugin))
+		gaim_plugin_unload(plugin);
+
+	plugins = g_list_remove(plugins, plugin);
+
+	/* XXX */
+	if (plugin->info != NULL && plugin->info->type == GAIM_PLUGIN_PROTOCOL)
+		return;
+
+	if (plugin->native_plugin) {
+
+		if (plugin->info != NULL && plugin->info->type == GAIM_PLUGIN_LOADER) {
+			GList *exts, *l, *next_l;
+			GaimPlugin *p2;
+
+			for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
+				 exts != NULL;
+				 exts = exts->next) {
+
+				for (l = gaim_plugins_get_all(); l != NULL; l = next_l) {
+					next_l = l->next;
+
+					p2 = l->data;
+
+					if (p2->path != NULL && is_so_file(p2->path, exts->data))
+						gaim_plugin_destroy(p2);
+				}
+			}
+
+			g_list_free(GAIM_PLUGIN_LOADER_INFO(plugin)->exts);
+
+			plugin_loaders = g_list_remove(plugin_loaders, plugin);
+		}
+
+		if (plugin->info != NULL && plugin->info->destroy != NULL)
+			plugin->info->destroy(plugin);
+
+		if (plugin->handle != NULL)
+			g_module_close(plugin->handle);
+	}
+	else {
+		GaimPlugin *loader;
+		GaimPluginLoaderInfo *loader_info;
+
+		loader = __find_loader_for_plugin(plugin);
+
+		if (loader == NULL)
+			return;
+
+		loader_info = GAIM_PLUGIN_LOADER_INFO(loader);
+
+		if (loader_info->destroy != NULL)
+			loader_info->destroy(plugin);
+	}
+
+	if (plugin->info != NULL && plugin->info->dependencies != NULL)
+		g_list_free(plugin->info->dependencies);
+
+	if (plugin->path  != NULL) g_free(plugin->path);
+	if (plugin->error != NULL) g_free(plugin->error);
+
+	g_free(plugin);
+}
+
+gboolean
+gaim_plugin_is_loaded(const GaimPlugin *plugin)
+{
+	g_return_val_if_fail(plugin != NULL, FALSE);
+
+	return plugin->loaded;
+}
+
+void
+gaim_plugins_set_search_paths(size_t count, char **paths)
+{
+	size_t s;
+
+	g_return_if_fail(count > 0);
+	g_return_if_fail(paths != NULL);
+
+	if (search_paths != NULL) {
+		for (s = 0; s < search_path_count; s++)
+			g_free(search_paths[s]);
+
+		g_free(search_paths);
+	}
+
+	search_paths = g_new0(char *, count);
+
+	for (s = 0; s < count; s++) {
+		if (paths[s] == NULL)
+			search_paths[s] = NULL;
+		else
+			search_paths[s] = g_strdup(paths[s]);
+	}
+
+	search_path_count = count;
+}
+
+void
+gaim_plugins_unload_all(void)
+{
+#ifdef GAIM_PLUGINS
+
+	while (loaded_plugins != NULL)
+		gaim_plugin_unload(loaded_plugins->data);
+
+#endif /* GAIM_PLUGINS */
+}
+
+void
+gaim_plugins_probe(const char *ext)
+{
+#ifdef GAIM_PLUGINS
+	GDir *dir;
+	const gchar *file;
+	gchar *path;
+	GaimPlugin *plugin;
+	size_t i;
+
+	if (!g_module_supported())
+		return;
+
+	for (i = 0; i < search_path_count; i++) {
+		if (search_paths[i] == NULL)
+			continue;
+
+		dir = g_dir_open(search_paths[i], 0, NULL);
+
+		if (dir != NULL) {
+			while ((file = g_dir_read_name(dir)) != NULL) {
+				path = g_build_filename(search_paths[i], file, NULL);
+
+				if (ext == NULL || is_so_file(file, ext))
+					plugin = gaim_plugin_probe(path);
+
+				g_free(path);
+			}
+
+			g_dir_close(dir);
+		}
+	}
+
+	if (probe_cb != NULL)
+		probe_cb(probe_cb_data);
+
+#endif /* GAIM_PLUGINS */
+}
+
+gboolean
+gaim_plugin_register(GaimPlugin *plugin)
+{
+#ifdef GAIM_PLUGINS
+	g_return_val_if_fail(plugin != NULL, FALSE);
+
+	if (g_list_find(plugins, plugin))
+		return TRUE;
+
+	/* Special exception for loader plugins. We want them loaded NOW! */
+	if (plugin->info->type == GAIM_PLUGIN_LOADER) {
+		GList *exts;
+
+		/* We'll just load this right now. */
+		if (!gaim_plugin_load(plugin)) {
+
+			gaim_plugin_destroy(plugin);
+
+			return FALSE;
+		}
+
+		plugin_loaders = g_list_append(plugin_loaders, plugin);
+
+		for (exts = GAIM_PLUGIN_LOADER_INFO(plugin)->exts;
+			 exts != NULL;
+			 exts = exts->next) {
+
+			gaim_plugins_probe(exts->data);
+		}
+	}
+	else if (plugin->info->type == GAIM_PLUGIN_PROTOCOL) {
+
+		/* We'll just load this right now. */
+		if (!gaim_plugin_load(plugin)) {
+
+			gaim_plugin_destroy(plugin);
+
+			return FALSE;
+		}
+	
+		if (GAIM_PLUGIN_PROTOCOL_INFO(plugin)->protocol == GAIM_PROTO_ICQ ||
+			gaim_find_prpl(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->protocol)) {
+
+			/* Nothing to see here--move along, move along */
+			gaim_plugin_destroy(plugin);
+
+			return FALSE;
+		}
+
+		protocols = g_slist_insert_sorted(protocols, plugin,
+										  (GCompareFunc)compare_prpl);
+	}
+
+	plugins = g_list_append(plugins, plugin);
+
+	return TRUE;
+#else
+	return FALSE;
+#endif /* !GAIM_PLUGINS */
+}
+
+gboolean
+gaim_plugins_enabled(void)
+{
+#ifdef GAIM_PLUGINS
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
+void
+gaim_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
+{
+	/* TODO */
+	probe_cb = func;
+	probe_cb_data = data;
+}
+
+void
+gaim_plugins_unregister_probe_notify_cb(void (*func)(void *))
+{
+	/* TODO */
+	probe_cb = NULL;
+	probe_cb_data = NULL;
+}
+
+void
+gaim_plugins_register_load_notify_cb(void (*func)(GaimPlugin *, void *),
+									 void *data)
+{
+	/* TODO */
+	load_cb = func;
+	load_cb_data = data;
+}
+
+void
+gaim_plugins_unregister_load_notify_cb(void (*func)(GaimPlugin *, void *))
+{
+	/* TODO */
+	load_cb = NULL;
+	load_cb_data = NULL;
+}
+
+void
+gaim_plugins_register_unload_notify_cb(void (*func)(GaimPlugin *, void *),
+									   void *data)
+{
+	/* TODO */
+	unload_cb = func;
+	unload_cb_data = data;
+}
+
+void
+gaim_plugins_unregister_unload_notify_cb(void (*func)(GaimPlugin *, void *))
+{
+	/* TODO */
+	unload_cb = NULL;
+	unload_cb_data = NULL;
+}
+
+GaimPlugin *
+gaim_plugins_find_with_name(const char *name)
+{
+	GaimPlugin *plugin;
+	GList *l;
+
+	for (l = plugins; l != NULL; l = l->next) {
+		plugin = l->data;
+
+		if (!strcmp(plugin->info->name, name))
+			return plugin;
+	}
+
+	return NULL;
+}
+
+GaimPlugin *
+gaim_plugins_find_with_filename(const char *filename)
+{
+	GaimPlugin *plugin;
+	GList *l;
+
+	for (l = plugins; l != NULL; l = l->next) {
+		plugin = l->data;
+
+		if (plugin->path != NULL && !strcmp(plugin->path, filename))
+			return plugin;
+	}
+
+	return NULL;
+}
+
+GaimPlugin *
+gaim_plugins_find_with_id(const char *id)
+{
+	GaimPlugin *plugin;
+	GList *l;
+
+	g_return_val_if_fail(id != NULL, NULL);
+
+	for (l = plugins; l != NULL; l = l->next) {
+		plugin = l->data;
+
+		if (!strcmp(plugin->info->id, id))
+			return plugin;
+	}
+
+	return NULL;
+}
+
+GList *
+gaim_plugins_get_loaded(void)
+{
+	return loaded_plugins;
+}
+
+GList *
+gaim_plugins_get_all(void)
+{
+	return plugins;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugin.h	Fri Apr 25 06:47:33 2003 +0000
@@ -0,0 +1,352 @@
+/**
+ * @file plugin.h Plugin API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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
+ */
+#ifndef _GAIM_PLUGIN_H_
+#define _GAIM_PLUGIN_H_
+
+typedef enum   _GaimPluginType GaimPluginType;     /**< GaimPluginType   */
+typedef struct _GaimPlugin     GaimPlugin;         /**< GaimPlugin       */
+typedef struct _GaimPluginInfo GaimPluginInfo;     /**< GaimPluginInfo   */
+typedef struct _GaimPluginLoaderInfo GaimPluginLoaderInfo;
+
+typedef int GaimPluginPriority; /**< Plugin priority. */
+
+/* XXX */
+#include "config.h"
+#include "event.h"
+
+/**
+ * Plugin types.
+ */
+enum _GaimPluginType
+{
+	GAIM_PLUGIN_UNKNOWN  = -1,  /**< Unknown type.    */
+	GAIM_PLUGIN_STANDARD = 0,   /**< Standard plugin. */
+	GAIM_PLUGIN_LOADER,         /**< Loader plugin.   */
+	GAIM_PLUGIN_PROTOCOL        /**< Protocol plugin. */
+};
+
+#define GAIM_PRIORITY_DEFAULT     0
+#define GAIM_PRIORITY_HIGHEST  9999
+#define GAIM_PRIORITY_LOWEST  -9999
+
+#define GAIM_PLUGIN_ID_UNSET { 0, 0, 0, 0 }
+
+/**
+ * Detailed information about a plugin.
+ *
+ * This is used in the version 2.0 API and up.
+ */
+struct _GaimPluginInfo
+{
+	unsigned int api_version;
+	GaimPluginType type;
+	char *ui_requirement;
+	unsigned long flags;
+	GList *dependencies;
+	GaimPluginPriority priority;
+
+	char *id;
+	char *name;
+	char *version;
+	char *summary;
+	char *description;
+	char *author;
+	char *homepage;
+
+	gboolean (*load)(GaimPlugin *plugin);
+	gboolean (*unload)(GaimPlugin *plugin);
+	void (*destroy)(GaimPlugin *plugin);
+
+	void *ui_info;
+	void *extra_info;
+};
+
+/**
+ * Extra information for loader plugins.
+ */
+struct _GaimPluginLoaderInfo
+{
+	GList *exts;
+
+	gboolean (*probe)(GaimPlugin *plugin);
+	gboolean (*load)(GaimPlugin *plugin);
+	gboolean (*unload)(GaimPlugin *plugin);
+	void     (*destroy)(GaimPlugin *plugin);
+
+	GaimSignalBroadcastFunc broadcast;
+};
+
+/**
+ * A plugin handle.
+ */
+struct _GaimPlugin
+{
+	gboolean native_plugin;                /**< Native C plugin.          */
+	gboolean loaded;                       /**< The loaded state.         */
+	void *handle;                          /**< The module handle.        */
+	char *path;                            /**< The path to the plugin.   */
+	GaimPluginInfo *info;                  /**< The plugin information.   */
+	char *error;
+	void *extra;                           /**< Plugin-specific data.     */
+};
+
+#define GAIM_PLUGIN_LOADER_INFO(plugin) \
+	((GaimPluginLoaderInfo *)(plugin)->info->extra_info)
+
+/**
+ * Handles the initialization of modules.
+ */
+#ifdef STATIC_MODULE
+# define GAIM_INIT_PLUGIN(pluginname, initfunc, plugininfo) \
+	gboolean gaim_init_##pluginname##_plugin(void) { \
+		GaimPlugin *plugin = gaim_plugin_new(TRUE, NULL); \
+		plugin->info = &(plugininfo); \
+		initfunc((plugin)); \
+		return gaim_plugin_register(plugin); \
+	}
+#else /* if !STATIC_MODULE */
+# define GAIM_INIT_PLUGIN(pluginname, initfunc, plugininfo) \
+	gboolean gaim_init_plugin(GaimPlugin *plugin) { \
+		plugin->info = &(plugininfo); \
+		initfunc((plugin)); \
+		return gaim_plugin_register(plugin); \
+	}
+#endif
+
+/**************************************************************************/
+/** @name Plugin namespace                                                */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates a new plugin structure.
+ *
+ * @param native Whether or not the plugin is native.
+ * @param path   The path to the plugin, or @c NULL if statically compiled.
+ *
+ * @return A new GaimPlugin structure.
+ */
+GaimPlugin *gaim_plugin_new(gboolean native, const char *path);
+
+/**
+ * Probes a plugin, retrieving the information on it and adding it to the
+ * list of available plugins.
+ *
+ * @param filename The plugin's filename.
+ *
+ * @return The plugin handle.
+ *
+ * @see gaim_plugin_load()
+ * @see gaim_plugin_destroy()
+ */
+GaimPlugin *gaim_plugin_probe(const char *filename);
+
+/**
+ * Registers a plugin and prepares it for loading.
+ *
+ * This shouldn't be called by anything but the internal module code.
+ *
+ * @param plugin The plugin to register.
+ */
+gboolean gaim_plugin_register(GaimPlugin *plugin);
+
+/**
+ * Attempts to load a previously probed plugin.
+ *
+ * @param filename The plugin's filename.
+ *
+ * @return @c TRUE if successful, or @c FALSE otherwise.
+ *
+ * @see gaim_plugin_reload()
+ * @see gaim_plugin_unload()
+ */
+gboolean gaim_plugin_load(GaimPlugin *plugin);
+
+/**
+ * Unloads the specified plugin.
+ *
+ * @param plugin The plugin handle.
+ *
+ * @return @c TRUE if successful, or @c FALSE otherwise.
+ *
+ * @see gaim_plugin_load()
+ * @see gaim_plugin_reload()
+ */
+gboolean gaim_plugin_unload(GaimPlugin *plugin);
+
+/**
+ * Reloads a plugin.
+ *
+ * @param plugin The old plugin handle.
+ * 
+ * @return @c TRUE if successful, or @c FALSE otherwise.
+ *
+ * @see gaim_plugin_load()
+ * @see gaim_plugin_unload()
+ */
+gboolean gaim_plugin_reload(GaimPlugin *plugin);
+
+/**
+ * Unloads a plugin and destroys the structure from memory.
+ *
+ * @param plugin The plugin handle.
+ */
+void gaim_plugin_destroy(GaimPlugin *plugin);
+
+/**
+ * Returns whether or not a plugin is currently loaded.
+ *
+ * @param plugin The plugin.
+ *
+ * @return TRUE if loaded, or FALSE otherwise.
+ */
+gboolean gaim_plugin_is_loaded(const GaimPlugin *plugin);
+
+/*@}*/
+
+/**************************************************************************/
+/** @name Plugins namespace                                               */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Sets the search paths for plugins.
+ *
+ * @param count The number of search paths.
+ * @param paths The search paths.
+ */
+void gaim_plugins_set_search_paths(size_t count, char **paths);
+
+/**
+ * Unloads all registered plugins.
+ */
+void gaim_plugins_unload_all(void);
+
+/**
+ * Probes for plugins in the registered module paths.
+ *
+ * @param ext The extension type to probe for, or @c NULL for all.
+ *
+ * @see gaim_plugin_set_probe_path()
+ */
+void gaim_plugins_probe(const char *ext);
+
+/**
+ * Returns whether or not plugin support is enabled.
+ *
+ * @return TRUE if plugin support is enabled, or FALSE otherwise.
+ */
+gboolean gaim_plugins_enabled(void);
+
+/**
+ * Registers a function that will be called when probing is finished.
+ *
+ * @param func The callback function.
+ * @param data Data to pass to the callback.
+ */
+void gaim_plugins_register_probe_notify_cb(void (*func)(void *), void *data);
+
+/**
+ * Unregisters a function that would be called when probing is finished.
+ *
+ * @param func The callback function.
+ */
+void gaim_plugins_unregister_probe_notify_cb(void (*func)(void *));
+
+/**
+ * Registers a function that will be called when a plugin is loaded.
+ *
+ * @param func The callback functino.
+ * @param data Data to pass to the callback.
+ */
+void gaim_plugins_register_load_notify_cb(void (*func)(GaimPlugin *, void *),
+										  void *data);
+
+/**
+ * Unregisters a function that would be called when a plugin is loaded.
+ *
+ * @param func The callback functino.
+ */
+void gaim_plugins_unregister_load_notify_cb(void (*func)(GaimPlugin *, void *));
+
+/**
+ * Registers a function that will be called when a plugin is unloaded.
+ *
+ * @param func The callback functino.
+ * @param data Data to pass to the callback.
+ */
+void gaim_plugins_register_unload_notify_cb(void (*func)(GaimPlugin *, void *),
+											void *data);
+
+/**
+ * Unregisters a function that would be called when a plugin is unloaded.
+ *
+ * @param func The callback functino.
+ */
+void gaim_plugins_unregister_unload_notify_cb(void (*func)(GaimPlugin *,
+														   void *));
+
+/**
+ * Finds a plugin with the specified name.
+ *
+ * @param name The plugin name.
+ *
+ * @return The plugin if found, or @c NULL if not found.
+ */
+GaimPlugin *gaim_plugins_find_with_name(const char *name);
+
+/**
+ * Finds a plugin with the specified filename.
+ *
+ * @param filename The plugin filename.
+ *
+ * @return The plugin if found, or @c NULL if not found.
+ */
+GaimPlugin *gaim_plugins_find_with_filename(const char *filename);
+
+/**
+ * Finds a plugin with the specified plugin ID.
+ *
+ * @param id The plugin ID.
+ *
+ * @return The plugin if found, or @c NULL if not found.
+ */
+GaimPlugin *gaim_plugins_find_with_id(const char *id);
+
+/**
+ * Returns a list of all loaded plugins.
+ *
+ * @return A list of all plugins.
+ */
+GList *gaim_plugins_get_loaded(void);
+
+/**
+ * Returns a list of all plugins, whether loaded or not.
+ *
+ * @return A list of all plugins.
+ */
+GList *gaim_plugins_get_all(void);
+
+/*@}*/
+
+#endif /* _GAIM_PLUGIN_H_ */
--- a/src/prefs.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/prefs.c	Fri Apr 25 06:47:33 2003 +0000
@@ -37,6 +37,7 @@
 #include "gtkimhtml.h"
 #include "gaim.h"
 #include "gtklist.h"
+#include "gtkplugin.h"
 #include "prpl.h"
 #include "proxy.h"
 #include "sound.h"
@@ -80,12 +81,16 @@
 /*
  * PROTOTYPES
  */
-GtkTreeIter *prefs_notebook_add_page(char*, GdkPixbuf*, GtkWidget*, GtkTreeIter*, GtkTreeIter*, int);
+GtkTreeIter *prefs_notebook_add_page(const char*, GdkPixbuf*, GtkWidget*, GtkTreeIter*, GtkTreeIter*, int);
+
+static void update_plugin_list(void *data);
 
 void delete_prefs(GtkWidget *asdf, void *gdsa) {
-	GList *l = plugins;
-	struct gaim_plugin *plug;
-	
+	GList *l;
+	GaimPlugin *plug;
+
+	gaim_plugins_unregister_probe_notify_cb(update_plugin_list);
+
 	save_prefs();
 	prefs = NULL;
 	tree_v = NULL;
@@ -99,13 +104,20 @@
 		gtk_widget_destroy(sounddialog);
 	g_object_unref(G_OBJECT(prefs_away_store));
 	prefs_away_store = NULL;
-	while(l) {
+
+	for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) {
 		plug = l->data;
-		if(plug->iter) {
-			g_free(plug->iter);
-			plug->iter = NULL;
+
+		if (GAIM_IS_GTK_PLUGIN(plug)) {
+			GaimGtkPluginUiInfo *ui_info;
+
+			ui_info = GAIM_GTK_PLUGIN_UI_INFO(plug);
+
+			if (ui_info->iter != NULL) {
+				g_free(ui_info->iter);
+				ui_info->iter = NULL;
+			}
 		}
-		l = l->next;
 	}
 }
 
@@ -1113,27 +1125,38 @@
 	return ret;
 }
 
-#if USE_PLUGINS
-GtkWidget *plugin_description=NULL, *plugin_details=NULL;
+static GtkWidget *plugin_description=NULL, *plugin_details=NULL;
+
 static void prefs_plugin_sel (GtkTreeSelection *sel, GtkTreeModel *model) 
 {
 	gchar buf[1024];
 	GtkTreeIter  iter;
 	GValue val = { 0, };
-	struct gaim_plugin *plug;
+	GaimPlugin *plug;
 
 	if (! gtk_tree_selection_get_selected (sel, &model, &iter))
 		return;
 	gtk_tree_model_get_value (model, &iter, 2, &val);
 	plug = g_value_get_pointer(&val);
 	
-	if (plug->error[0]) 
-		g_snprintf(buf, sizeof(buf), "<span size=\"larger\">%s %s</span>\n\n"
-					       "<span weight=\"bold\" color=\"red\">%s</span>\n\n"
-					       "%s", plug->desc.name, plug->desc.version, plug->error, plug->desc.description); 
-	else
-		g_snprintf(buf, sizeof(buf), "<span size=\"larger\">%s %s</span>\n\n"
-					       "%s", plug->desc.name, plug->desc.version, plug->desc.description); 
+	if (plug->error != NULL) {
+		g_snprintf(buf, sizeof(buf),
+				   "<span size=\"larger\">%s %s</span>\n\n"
+				   "<span weight=\"bold\" color=\"red\">%s</span>\n\n"
+				   "%s",
+				   g_markup_escape_text(_(plug->info->name), -1),
+				   plug->info->version,
+				   g_markup_escape_text(plug->error, -1),
+				   g_markup_escape_text(_(plug->info->description), -1));
+	}
+	else {
+		g_snprintf(buf, sizeof(buf),
+				   "<span size=\"larger\">%s %s</span>\n\n%s",
+				   g_markup_escape_text(_(plug->info->name), -1),
+				   plug->info->version,
+				   g_markup_escape_text(_(plug->info->description), -1));
+	}
+
 	gtk_label_set_markup(GTK_LABEL(plugin_description), buf);
 	g_snprintf(buf, sizeof(buf), 
 #ifndef _WIN32
@@ -1147,7 +1170,12 @@
 		     "<span weight=\"bold\">URL:</span>  %s\n"
 		     "<span weight=\"bold\">File name:</span>  %s"),
 #endif
-		   plug->desc.name, plug->desc.version, plug->desc.authors, plug->desc.url, plug->path);
+		   g_markup_escape_text(_(plug->info->name), -1),
+		   plug->info->version,
+		   g_markup_escape_text(_(plug->info->author), -1),
+		   g_markup_escape_text(plug->info->homepage, -1),
+		   plug->path);
+
 	gtk_label_set_markup(GTK_LABEL(plugin_details), buf);
 	g_value_unset (&val);
 }
@@ -1157,9 +1185,8 @@
 	GtkTreeModel *model = (GtkTreeModel *)data;
 	GtkTreeIter iter;
 	GtkTreePath *path = gtk_tree_path_new_from_string(pth);
-	struct gaim_plugin *plug;
+	GaimPlugin *plug;
 	gchar buf[1024];
-	GtkWidget *(*config)();
 	
 	GdkCursor *wait = gdk_cursor_new (GDK_WATCH);
 	gdk_window_set_cursor(prefs->window, wait);
@@ -1168,85 +1195,131 @@
 	gtk_tree_model_get_iter (model, &iter, path);
 	gtk_tree_model_get (model, &iter, 2, &plug, -1);
 	
-	if (!plug->handle)
-
-		if (plug->type == plugin)
-#ifdef GAIM_PLUGINS
-			{
-				load_plugin(plug->path);
-				if (g_module_symbol(plug->handle, "gaim_plugin_config_gtk", (gpointer *)&config)) {
-					plug->iter = g_new0(GtkTreeIter, 1);
-					prefs_notebook_add_page(plug->desc.name, NULL, config(), plug->iter, &plugin_iter, notebook_page++);
-					if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(prefstree), &plugin_iter) == 1) {
-						/* Expand the tree for the first plugin added */
-						GtkTreePath *path2  = gtk_tree_model_get_path(GTK_TREE_MODEL(prefstree), &plugin_iter);
-						gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_v), path2, TRUE);
-						gtk_tree_path_free (path2);
-					}
+	if (!gaim_plugin_is_loaded(plug)) {
+		gaim_plugin_load(plug);
+
+		/*
+		 * NOTE: This is basically the same check as before
+		 *       (plug->type == plugin), but now there aren't plugin types.
+		 *       Not yet, anyway. I want to do a V2 of the plugin API.
+		 *       The thing is, we should have a flag specifying the UI type,
+		 *       or just whether it's a general plugin or a UI-specific
+		 *       plugin. We should only load this if it's UI-specific.
+		 *
+		 *         -- ChipX86
+		 */
+		if (GAIM_IS_GTK_PLUGIN(plug))
+		{
+			GtkWidget *config_frame;
+			GaimGtkPluginUiInfo *ui_info;
+
+			ui_info = GAIM_GTK_PLUGIN_UI_INFO(plug);
+			config_frame = gaim_gtk_plugin_get_config_frame(plug);
+
+			if (config_frame != NULL) {
+				ui_info->iter = g_new0(GtkTreeIter, 1);
+				prefs_notebook_add_page(_(plug->info->name), NULL,
+										config_frame, ui_info->iter,
+										&plugin_iter, notebook_page++);
+
+				if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(prefstree),
+												   &plugin_iter) == 1) {
+
+					/* Expand the tree for the first plugin added */
+					GtkTreePath *path2;
+					
+					path2 = gtk_tree_model_get_path(GTK_TREE_MODEL(prefstree),
+													&plugin_iter);
+					gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_v),
+											 path2, TRUE);
+					gtk_tree_path_free(path2);
 				}
 			}
-#else
-	        {}	
-#endif
-		else
-#ifdef USE_PERL
-			perl_load_file(plug->path);
-#else
-	        {}
-#endif 
-	else
-		if (plug->type == plugin)
-#ifdef GAIM_PLUGINS
-			{
-				unload_plugin(plug);
-				if (plug->iter) {
-					gtk_tree_store_remove(GTK_TREE_STORE(prefstree), plug->iter);
-					g_free(plug->iter);
-					plug->iter = NULL;
-				}
+		}
+	}
+	else {
+		if (GAIM_IS_GTK_PLUGIN(plug)) {
+			GaimGtkPluginUiInfo *ui_info;
+
+			ui_info = GAIM_GTK_PLUGIN_UI_INFO(plug);
+
+			if (ui_info != NULL && ui_info->iter != NULL) {
+				gtk_tree_store_remove(GTK_TREE_STORE(prefstree), ui_info->iter);
+				g_free(ui_info->iter);
+				ui_info->iter = NULL;
 			}
-#else
-	                {} 
-#endif
-		else
-#ifdef USE_PERL
-			perl_unload_file(plug);
-#else
-	                {}
-#endif
+		}
+
+		gaim_plugin_unload(plug);
+	}
+
 	gdk_window_set_cursor(prefs->window, NULL);
-	if (plug->error[0]) 
-		g_snprintf(buf, sizeof(buf), "<span size=\"larger\">%s %s</span>\n\n"
-					       "<span weight=\"bold\" color=\"red\">%s</span>\n\n"
-					       "%s", plug->desc.name, plug->desc.version, plug->error, plug->desc.description); 
-	else
-		g_snprintf(buf, sizeof(buf), "<span size=\"larger\">%s %s</span>\n\n"
-					       "%s", plug->desc.name, plug->desc.version, plug->desc.description); 
+
+	if (plug->error != NULL) {
+		g_snprintf(buf, sizeof(buf),
+				   "<span size=\"larger\">%s %s</span>\n\n"
+				   "<span weight=\"bold\" color=\"red\">%s</span>\n\n"
+				   "%s",
+				   g_markup_escape_text(_(plug->info->name), -1),
+				   plug->info->version,
+				   g_markup_escape_text(plug->error, -1),
+				   g_markup_escape_text(_(plug->info->description), -1));
+	}
+	else {
+		g_snprintf(buf, sizeof(buf),
+				   "<span size=\"larger\">%s %s</span>\n\n%s",
+				   g_markup_escape_text(_(plug->info->name), -1),
+				   plug->info->version,
+				   g_markup_escape_text(_(plug->info->description), -1));
+	}
+
 	gtk_label_set_markup(GTK_LABEL(plugin_description), buf);
-	gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, plug->handle, -1);
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0,
+						gaim_plugin_is_loaded(plug), -1);
 	
 	gtk_label_set_markup(GTK_LABEL(plugin_description), buf);
 	gtk_tree_path_free(path);
 }
 
+static void
+update_plugin_list(void *data)
+{
+	GtkListStore *ls = GTK_LIST_STORE(data);
+	GtkTreeIter iter;
+	GList *probes;
+	GaimPlugin *plug;
+
+	gtk_list_store_clear(ls);
+
+	for (probes = gaim_plugins_get_all();
+		 probes != NULL;
+		 probes = probes->next) {
+
+		plug = probes->data;
+
+		if (plug->info->type != GAIM_PLUGIN_STANDARD)
+			continue;
+
+		gtk_list_store_append (ls, &iter);
+		gtk_list_store_set(ls, &iter,
+				   0, gaim_plugin_is_loaded(plug),
+				   1, plug->info->name ? _(plug->info->name) : plug->path, 
+				   2, plug, -1);
+	}
+}
+
 static GtkWidget *plugin_page ()
 {
 	GtkWidget *ret;
-
 	GtkWidget *sw, *vp;
-	GtkTreeIter iter;
 	GtkWidget *event_view;
 	GtkListStore *ls;
 	GtkCellRenderer *rend, *rendt;
 	GtkTreeViewColumn *col;
 	GtkTreeSelection *sel;
 	GtkTreePath *path;
-	
 	GtkWidget *nb;
 
-	GList *probes = probed_plugins;
-	struct gaim_plugin *plug;
-	
 	ret = gtk_vbox_new(FALSE, 18);
 	gtk_container_set_border_width (GTK_CONTAINER (ret), 12);
 
@@ -1257,16 +1330,9 @@
 	gtk_box_pack_start(GTK_BOX(ret), sw, TRUE, TRUE, 0);
 
 	ls = gtk_list_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
-	while (probes) {
-		plug = probes->data;
-		gtk_list_store_append (ls, &iter);
-		gtk_list_store_set(ls, &iter,
-				   0, plug->handle,
-				   1, plug->desc.name ? plug->desc.name : plug->path, 
-				   2, plug, -1);
-		probes = probes->next;
-	}
-	
+
+	update_plugin_list(ls);
+
 	event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(ls));
 
 	rend = gtk_cell_renderer_toggle_new();
@@ -1340,10 +1406,11 @@
 	gtk_tree_selection_select_path(sel, path);
 	gtk_tree_path_free(path);
 
+	gaim_plugins_register_probe_notify_cb(update_plugin_list, ls);
+
 	gtk_widget_show_all(ret);
 	return ret;
 }
-#endif
 
 static void event_toggled (GtkCellRendererToggle *cell, gchar *pth, gpointer data)
 {
@@ -1678,7 +1745,7 @@
 	return ret;
 }
 
-GtkTreeIter *prefs_notebook_add_page(char *text,
+GtkTreeIter *prefs_notebook_add_page(const char *text,
 				     GdkPixbuf *pixbuf,
 				     GtkWidget *page,
 				     GtkTreeIter *iter,
@@ -1702,11 +1769,8 @@
 
 void prefs_notebook_init() {
 	GtkTreeIter p, p2, c;
-#if USE_PLUGINS
-	GtkWidget *(*config)();
-	GList *l = plugins;
-	struct gaim_plugin *plug;
-#endif
+	GList *l;
+	GaimPlugin *plug;
 	prefs_notebook_add_page(_("Interface"), NULL, interface_page(), &p, NULL, notebook_page++);
 	prefs_notebook_add_page(_("Smiley Themes"), NULL, theme_page(), &c, &p, notebook_page++);
 	prefs_notebook_add_page(_("Fonts"), NULL, font_page(), &c, &p, notebook_page++);
@@ -1727,17 +1791,29 @@
 	prefs_notebook_add_page(_("Sound Events"), NULL, sound_events_page(), &c, &p, notebook_page++);
 	prefs_notebook_add_page(_("Away / Idle"), NULL, away_page(), &p, NULL, notebook_page++);
 	prefs_notebook_add_page(_("Away Messages"), NULL, away_message_page(), &c, &p, notebook_page++);
-#if USE_PLUGINS
-	prefs_notebook_add_page(_("Plugins"), NULL, plugin_page(), &plugin_iter, NULL, notebook_page++);
-	while (l) {
-		plug = l->data;
-		if (plug->type == plugin && g_module_symbol(plug->handle, "gaim_plugin_config_gtk", (gpointer *)&config)) {
-			plug->iter = g_new0(GtkTreeIter, 1);
-			prefs_notebook_add_page(plug->desc.name, NULL, config(), plug->iter, &plugin_iter, notebook_page++);
+
+	if (gaim_plugins_enabled()) {
+		prefs_notebook_add_page(_("Plugins"), NULL, plugin_page(), &plugin_iter, NULL, notebook_page++);
+
+		for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) {
+			plug = l->data;
+
+			if (GAIM_IS_GTK_PLUGIN(plug)) {
+				GtkWidget *config_frame;
+				GaimGtkPluginUiInfo *ui_info;
+
+				ui_info = GAIM_GTK_PLUGIN_UI_INFO(plug);
+				config_frame = gaim_gtk_plugin_get_config_frame(plug);
+
+				if (config_frame != NULL) {
+					ui_info->iter = g_new0(GtkTreeIter, 1);
+					prefs_notebook_add_page(_(plug->info->name), NULL,
+											config_frame, ui_info->iter,
+											&plugin_iter, notebook_page++);
+				}
+			}
 		}
-		l = l->next;
 	}
-#endif
 }
 
 void show_prefs()
--- a/src/protocols/Makefile.am	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/Makefile.am	Fri Apr 25 06:47:33 2003 +0000
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = gg icq irc jabber msn napster oscar toc yahoo zephyr
+DIST_SUBDIRS = gg irc jabber msn napster oscar toc yahoo zephyr
 
 if PRPLS
 
--- a/src/protocols/gg/gg.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/gg/gg.c	Fri Apr 25 06:47:33 2003 +0000
@@ -1,6 +1,6 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 5500 2003-04-15 04:18:00Z faceprint $
+ * $Id: gg.c 5573 2003-04-25 06:47:33Z chipx86 $
  *
  * Copyright (C) 2001 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  * 
@@ -1277,73 +1277,101 @@
 	/* It's implemented on client side because GG server doesn't support this */
 }
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
-G_MODULE_EXPORT void gg_init(struct prpl *ret)
+static GaimPluginProtocolInfo prpl_info =
+{
+	GAIM_PROTO_GADUGADU,
+	0,
+	NULL,
+	NULL,
+	agg_list_icon,
+	agg_list_emblems,
+	NULL,
+	NULL,
+	agg_away_states,
+	agg_actions,
+	agg_buddy_menu,
+	NULL,
+	agg_login,
+	agg_close,
+	agg_send_im,
+	NULL,
+	NULL,
+	agg_get_info,
+	agg_set_away,
+	NULL,
+	NULL,
+	NULL,
+	agg_dir_search,
+	NULL,
+	agg_change_passwd,
+	agg_add_buddy,
+	agg_add_buddies,
+	agg_rem_buddy,
+	NULL,
+	agg_permit_deny_dummy,
+	agg_permit_deny_dummy,
+	agg_permit_deny_dummy,
+	agg_permit_deny_dummy,
+	agg_set_permit_deny_dummy,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	agg_keepalive,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-gg",		                                  /**< id             */
+	"Gadu-Gadu",                                      /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Gadu-Gadu Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Gadu-Gadu Protocol Plugin"),
+	"Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>",       /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
 {
 	struct proto_user_opt *puo;
-	ret->protocol = PROTO_GADUGADU;
-	ret->options = 0;
-	ret->name = g_strdup("Gadu-Gadu");
-	ret->list_icon = agg_list_icon;
-	ret->list_emblems = agg_list_emblems;
-	ret->away_states = agg_away_states;
-	ret->actions = agg_actions;
-	ret->buddy_menu = agg_buddy_menu;
-	ret->chat_info = NULL;
-	ret->login = agg_login;
-	ret->close = agg_close;
-	ret->send_im = agg_send_im;
-	ret->set_info = NULL;
-	ret->get_info = agg_get_info;
-	ret->set_away = agg_set_away;
-	ret->set_dir = NULL;
-	ret->get_dir = agg_get_info;
-	ret->dir_search = agg_dir_search;
-	ret->set_idle = NULL;
-	ret->change_passwd = agg_change_passwd;
-	ret->add_buddy = agg_add_buddy;
-	ret->add_buddies = agg_add_buddies;
-	ret->remove_buddy = agg_rem_buddy;
-	ret->add_permit = agg_permit_deny_dummy;
-	ret->add_deny = agg_permit_deny_dummy;
-	ret->rem_permit = agg_permit_deny_dummy;
-	ret->rem_deny = agg_permit_deny_dummy;
-	ret->set_permit_deny = agg_set_permit_deny_dummy;
-	ret->warn = NULL;
-	ret->join_chat = NULL;
-	ret->chat_invite = NULL;
-	ret->chat_leave = NULL;
-	ret->chat_whisper = NULL;
-	ret->chat_send = NULL;
-	ret->keepalive = agg_keepalive;
-	ret->normalize = NULL;
-     
+
 	puo = g_new0(struct proto_user_opt, 1);
-	puo->label = g_strdup(_("Nick:"));
-	puo->def = g_strdup(_("Gadu-Gadu User"));
-	puo->pos = USEROPT_NICK;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
+	puo->label = g_strdup("Nick:");
+	puo->def   = g_strdup("Gadu-Gadu User");
+	puo->pos   = USEROPT_NICK;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
 
-	my_protocol = ret;
+	my_protocol = plugin;
 }
 
-#ifndef STATIC
-
-G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
-{
-	gg_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
-}
-
-#endif
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
+GAIM_INIT_PLUGIN(gg, __init_plugin, info);
--- a/src/protocols/icq/gaim_icq.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/icq/gaim_icq.c	Fri Apr 25 06:47:33 2003 +0000
@@ -510,6 +510,6 @@
 G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
 {
 	icq_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
+	prpl->plug->desc.api_version = GAIM_PLUGIN_API_VERSION;
 }
 #endif
--- a/src/protocols/irc/irc.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/irc/irc.c	Fri Apr 25 06:47:33 2003 +0000
@@ -59,7 +59,7 @@
 
 #define DEFAULT_SERVER "irc.freenode.net"
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
 /* for win32 compatability */
 G_MODULE_IMPORT GSList *connections;
@@ -1360,7 +1360,7 @@
 		dccchat->port=atoi(chat_args[4]);		
 		g_snprintf(dccchat->nick, sizeof(dccchat->nick), nick);	
 		g_snprintf(ask, sizeof(ask), _("%s would like to establish a DCC chat"), nick);
-		do_ask_dialog(ask, _("This requires a direct connection to be established between the two computers.  Messages sent will not pass through the IRC server"), dccchat, _("Connect"), dcc_chat_init, _("Cancel"), dcc_chat_cancel, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+		do_ask_dialog(ask, _("This requires a direct connection to be established between the two computers.  Messages sent will not pass through the IRC server"), dccchat, _("Connect"), dcc_chat_init, _("Cancel"), dcc_chat_cancel, my_protocol->handle, FALSE);
 	}
 
 
@@ -2833,66 +2833,112 @@
 	return m;
 }
 
-G_MODULE_EXPORT void 
-irc_init(struct prpl *ret)
+static GaimPluginProtocolInfo prpl_info =
 {
-	struct proto_user_split *pus;
+	GAIM_PROTO_IRC,
+	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL,
+	NULL,
+	NULL,
+	irc_list_icon,
+	irc_list_emblems,
+	NULL,
+	NULL,
+	irc_away_states,
+	NULL,
+	irc_buddy_menu,
+	irc_chat_info,
+	irc_login,
+	irc_close,
+	irc_send_im,
+	NULL,
+	NULL,
+	irc_get_info,
+	irc_set_away,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	irc_add_buddy,
+	NULL,
+	irc_remove_buddy,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	irc_join_chat,
+	irc_chat_invite,
+	irc_chat_leave,
+	NULL,
+	irc_chat_send,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	irc_convo_closed,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-irc",                                       /**< id             */
+	"IRC",                                            /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("IRC Protocol Plugin"),
+	                                                  /**  description    */
+	N_("IRC Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 	struct proto_user_opt *puo;
-	ret->protocol = PROTO_IRC;
-	ret->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL;
-	ret->name = g_strdup("IRC");
-	ret->list_icon = irc_list_icon;
-	ret->list_emblems = irc_list_emblems;
-	ret->login = irc_login;
-	ret->close = irc_close;
-	ret->send_im = irc_send_im;
-	ret->add_buddy = irc_add_buddy;
-	ret->remove_buddy = irc_remove_buddy;
-	ret->chat_info = irc_chat_info;
-	ret->join_chat = irc_join_chat;
-	ret->chat_leave = irc_chat_leave;
-	ret->chat_send = irc_chat_send;
-	ret->away_states = irc_away_states;
-	ret->set_away = irc_set_away;
-	ret->get_info = irc_get_info;
-	ret->buddy_menu = irc_buddy_menu;
-	ret->chat_invite = irc_chat_invite;
-	ret->convo_closed = irc_convo_closed;
-#if 0
-	ret->file_transfer_out = irc_file_transfer_out; 
-	ret->file_transfer_in = irc_file_transfer_in;
-	ret->file_transfer_data_chunk = irc_file_transfer_data_chunk;
-	ret->file_transfer_done = irc_file_transfer_done;
-	ret->file_transfer_cancel =irc_file_transfer_cancel;
-#endif
+	struct proto_user_split *pus;
 
 	pus = g_new0(struct proto_user_split, 1);
 	pus->sep = '@';
 	pus->label = g_strdup(_("Server:"));
 	pus->def = g_strdup(DEFAULT_SERVER);
-	ret->user_splits = g_list_append(ret->user_splits, pus);
+	prpl_info.user_splits = g_list_append(prpl_info.user_splits, pus);
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Port:"));
 	puo->def = g_strdup("6667");
 	puo->pos = USEROPT_PORT;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Encoding:"));
 	puo->def = g_strdup("ISO-8859-1");
 	puo->pos = USEROPT_CHARSET;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
-
-	my_protocol = ret;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	my_protocol = plugin;
 }
 
-#ifndef STATIC
-G_MODULE_EXPORT void 
-gaim_prpl_init(struct prpl* prpl)
-{
-	irc_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
-}
-
-#endif
+GAIM_INIT_PLUGIN(irc, __init_plugin, info);
--- a/src/protocols/jabber/jabber.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/jabber/jabber.c	Fri Apr 25 06:47:33 2003 +0000
@@ -59,7 +59,7 @@
 #include "win32dep.h"
 #endif
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
 /* for win32 compatability */
 G_MODULE_IMPORT GSList *connections;
@@ -1648,7 +1648,7 @@
 
 		jap->gc = GJ_GC(gjc);
 		jap->user = g_strdup(Jid);
-		do_ask_dialog(msg, NULL, jap, _("Authorize"), jabber_accept_add, _("Deny"), jabber_deny_add, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+		do_ask_dialog(msg, NULL, jap, _("Authorize"), jabber_accept_add, _("Deny"), jabber_deny_add, my_protocol->handle, FALSE);
 
 		g_free(msg);
 		xmlnode_free(g);	/* Never needed it here anyway */
@@ -4243,101 +4243,118 @@
 	return m;
 }
 
-G_MODULE_EXPORT void jabber_init(struct prpl *ret)
+static GaimPluginProtocolInfo prpl_info =
 {
-	/* the NULL's aren't required but they're nice to have */
-	struct proto_user_split *pus;
+	GAIM_PROTO_JABBER,
+	OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_CHAT_TOPIC,
+	NULL,
+	NULL,
+	jabber_list_icon,
+	jabber_list_emblems,
+	jabber_status_text,
+	jabber_tooltip_text,
+	jabber_away_states,
+	jabber_actions,
+	jabber_buddy_menu,
+	jabber_chat_info,
+	jabber_login,
+	jabber_close,
+	jabber_send_im,
+	jabber_set_info,
+	jabber_send_typing,
+	jabber_get_info,
+	jabber_set_away,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	jabber_set_idle,
+	jabber_change_passwd,
+	jabber_add_buddy,
+	NULL,
+	jabber_remove_buddy,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	jabber_join_chat,
+	jabber_chat_invite,
+	jabber_chat_leave,
+	jabber_chat_whisper,
+	jabber_chat_send,
+	jabber_keepalive,
+	jabber_register_user,
+	jabber_get_cb_info,
+	jabber_get_cb_away_msg,
+	jabber_alias_buddy,
+	NULL,
+	jabber_rename_group,
+	NULL,
+	jabber_convo_closed,
+	jabber_normalize
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-jabber",                                    /**< id             */
+	"Jabber",                                         /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Jabber Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Jabber Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 	struct proto_user_opt *puo;
-	ret->protocol = PROTO_JABBER;
-	ret->options = OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_CHAT_TOPIC;
-	ret->name = g_strdup("Jabber");
-	ret->list_icon = jabber_list_icon;
-	ret->list_emblems = jabber_list_emblems;
-	ret->status_text = jabber_status_text;
-	ret->tooltip_text = jabber_tooltip_text;
-	ret->away_states = jabber_away_states;
-	ret->actions = jabber_actions;
-	ret->buddy_menu = jabber_buddy_menu;
-	ret->login = jabber_login;
-	ret->close = jabber_close;
-	ret->send_im = jabber_send_im;
-	ret->set_info = jabber_set_info;
-	ret->get_info = jabber_get_info;
-	ret->get_cb_info = jabber_get_cb_info;
-	ret->get_cb_away = jabber_get_cb_away_msg;
-	ret->set_away = jabber_set_away;
-	ret->set_dir = NULL;
-	ret->get_dir = NULL;
-	ret->dir_search = NULL;
-	ret->set_idle = jabber_set_idle;
-	ret->change_passwd = jabber_change_passwd;
-	ret->add_buddy = jabber_add_buddy;
-	ret->add_buddies = NULL;
-	ret->remove_buddy = jabber_remove_buddy;
-	ret->add_permit = NULL;
-	ret->add_deny = NULL;
-	ret->rem_permit = NULL;
-	ret->rem_deny = NULL;
-	ret->set_permit_deny = NULL;
-	ret->warn = NULL;
-	ret->chat_info = jabber_chat_info;
-	ret->join_chat = jabber_join_chat;
-	ret->chat_invite = jabber_chat_invite;
-	ret->chat_leave = jabber_chat_leave;
-	ret->chat_whisper = jabber_chat_whisper;
-	ret->chat_send = jabber_chat_send;
-	ret->keepalive = jabber_keepalive;
-	ret->normalize = jabber_normalize;
-	ret->register_user = jabber_register_user;
-	ret->alias_buddy = jabber_alias_buddy;
-	ret->group_buddy = jabber_group_change;
-	ret->send_typing = jabber_send_typing;
-	ret->convo_closed = jabber_convo_closed;
-	ret->rename_group = jabber_rename_group;
+	struct proto_user_split *pus;
 
 	pus = g_new0(struct proto_user_split, 1);
 	pus->sep = '@';
 	pus->label = g_strdup(_("Server:"));
 	pus->def = g_strdup("jabber.org");
-	ret->user_splits = g_list_append(ret->user_splits, pus);
+	prpl_info.user_splits = g_list_append(prpl_info.user_splits, pus);
 
 	pus = g_new0(struct proto_user_split, 1);
 	pus->sep = '/';
 	pus->label = g_strdup(_("Resource:"));
 	pus->def = g_strdup("Gaim");
-	ret->user_splits = g_list_append(ret->user_splits, pus);
+	prpl_info.user_splits = g_list_append(prpl_info.user_splits, pus);
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Port:"));
 	puo->def = g_strdup_printf("%d", DEFAULT_PORT);
 	puo->pos = USEROPT_PORT;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Connect Server:"));
 	puo->def = g_strdup("");
 	puo->pos = USEROPT_CONN_SERVER;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
-
-	my_protocol = ret;
-}
-
-#ifndef STATIC
-
-G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
-{
-	jabber_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	my_protocol = plugin;
 }
 
-#endif
-
-/*
- * Local variables:
- * c-indentation-style: k&r
- * c-basic-offset: 8
- * indent-tabs-mode: notnil
- * End:
- *
- * vim: shiftwidth=8:
- */
+GAIM_INIT_PLUGIN(jabber, __init_plugin, info);
--- a/src/protocols/msn/msn.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/msn/msn.c	Fri Apr 25 06:47:33 2003 +0000
@@ -28,7 +28,7 @@
 
 #define BUDDY_ALIAS_MAXLEN 388
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
 /* for win32 compatability */
 G_MODULE_IMPORT GSList *connections;
@@ -36,11 +36,6 @@
 static void msn_login_callback(gpointer, gint, GaimInputCondition);
 static void msn_login_xfr_connect(gpointer, gint, GaimInputCondition);
 
-#if 0
-static struct msn_file_transfer *find_mft_by_cookie(struct gaim_connection *gc,	unsigned long cookie);
-static struct msn_file_transfer *find_mft_by_xfer(struct gaim_connection *gc, struct file_transfer *xfer);
-#endif
-
 static char *msn_normalize(const char *s)
 {
 	static char buf[BUF_LEN];
@@ -366,7 +361,7 @@
 		g_snprintf(msg, sizeof(msg), _("The user %s (%s) wants to add %s to his or her buddy list."),
 				ap->user, ap->friend, ap->gc->username);
 
-		//	do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+		//	do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->handle, FALSE);
 	} else if (!g_ascii_strncasecmp(buf, "BLP", 3)) {
 		char *type, *tmp = buf;
 
@@ -515,7 +510,7 @@
 				ap->gc = gc;
                          
 				g_snprintf(msg, sizeof(msg), _("The user %s (%s) wants to add you to their buddy list"),ap->user, ap->friend);
-				do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+				do_ask_dialog(msg, NULL, ap, _("Authorize"), msn_accept_add, _("Deny"), msn_cancel_add, my_protocol->handle, FALSE);
 			}
 		    }
 			
@@ -1258,168 +1253,6 @@
 	return MSN_TYPING_SEND_TIMEOUT;
 }
 
-#if 0
-static void msn_file_transfer_cancel(struct gaim_connection *gc,
-									 struct file_transfer *xfer)
-{
-	struct msn_data *md = gc->proto_data;
-	struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
-	struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
-	char header[MSN_BUF_LEN];
-	char buf[MSN_BUF_LEN];
-
-	if (!ms || !mft)
-	{
-		debug_printf("Eep! Returning from msn_file_transfer_cancel early");
-		return;
-	}
-
-	g_snprintf(header, sizeof(header),
-			   "MIME-Version: 1.0\r\n"
-			   "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n"
-			   "Invitation-Command: CANCEL\r\n"
-			   "Invitation-Cookie: %lu\r\n"
-			   "Cancel-Code: REJECT\r\n",
-			   (unsigned long)mft->cookie);
-
-	g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s\r\n\r\n",
-			   ++ms->trId, strlen(header) + strlen("\r\n\r\n"),
-			   header);
-
-	md->file_transfers = g_slist_remove(md->file_transfers, mft);
-
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0)
-	{
-		debug_printf("Uh oh! Killing switch.\n");
-		msn_kill_switch(ms);
-	}
-}
-
-static void msn_file_transfer_in(struct gaim_connection *gc,
-								 struct file_transfer *xfer, int offset)
-{
-	struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
-	struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
-	char header[MSN_BUF_LEN];
-	char buf[MSN_BUF_LEN];
-
-	if (!ms || !mft)
-	{
-		debug_printf("Eep! Returning from msn_file_transfer_in early");
-		return;
-	}
-
-	g_snprintf(header, sizeof(header),
-			   "MIME-Version: 1.0\r\n"
-			   "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n"
-			   "Invitation-Command: ACCEPT\r\n"
-			   "Invitation-Cookie: %lu\r\n"
-			   "Launch-Application: FALSE\r\n"
-			   "Request-Data: IP-Address:\r\n",
-			   (unsigned long)mft->cookie);
-
-	g_snprintf(buf, sizeof(buf), "MSG %u N %d\r\n%s\r\n\r\n",
-			   ++ms->trId, strlen(header) + strlen("\r\n\r\n"),
-			   header);
-
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0) {
-		msn_kill_switch(ms);
-		return;
-	}
-
-	mft->xfer = xfer;
-}
-
-static void msn_file_transfer_out(struct gaim_connection *gc,
-								  struct file_transfer *xfer,
-								  const char *name, int totfiles, int totsize)
-{
-	struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
-	struct msn_switchboard *ms = msn_find_switch(gc, mft->sn);
-	char header[MSN_BUF_LEN];
-	char buf[MSN_BUF_LEN];
-	struct stat sb;
-
-	if (!ms)
-		return;
-
-	if (totfiles > 1)
-		return;
-
-	if (stat(name, &sb) == -1)
-		return;
-
-	mft->cookie = 1 + (guint32)(4294967295.0 * rand() / (RAND_MAX + 1.0));
-
-	g_snprintf(header, sizeof(header),
-		"MIME-Version: 1.0\r\n"
-		"Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
-		"Application-Name: File Transfer\r\n"
-		"Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
-		"Invitation-Command: INVITE\r\n"
-		"Invitation-Cookie: %lu\r\n"
-		"Application-File: %s\r\n"
-		"Application-FileSize: %ld\r\n",
-		(unsigned long)mft->cookie, name, sb.st_size);
-
-	g_snprintf(buf, sizeof(buf), "MSG %u A %d\r\n%s\r\n\r\n",
-			   ++ms->trId,
-			   strlen(header) + strlen("\r\n\r\n"),
-			   header);
-
-	if (msn_write(ms->fd, buf, strlen(buf)) < 0)
-		msn_kill_switch(ms);
-
-	debug_printf("\n");
-}
-
-static void msn_file_transfer_done(struct gaim_connection *gc,
-								   struct file_transfer *xfer)
-{
-	struct msn_data *md = (struct msn_data *)gc->proto_data;
-	struct msn_file_transfer *mft = find_mft_by_xfer(gc, xfer);
-	char buf[MSN_BUF_LEN];
-
-	g_snprintf(buf, sizeof(buf), "BYE 16777989\r\n");
-
-	msn_write(mft->fd, buf, strlen(buf));
-
-	md->file_transfers = g_slist_remove(md->file_transfers, mft);
-
-	gaim_input_remove(mft->inpa);
-
-	close(mft->fd);
-
-	g_free(mft->filename);
-	g_free(mft->sn);
-	g_free(mft);
-}
-
-static size_t msn_file_transfer_read(struct gaim_connection *gc,
-									 struct file_transfer *xfer, int fd,
-									 char **buf)
-{
-	unsigned char header[3];
-	size_t len, size;
-
-	if (read(fd, header, sizeof(header)) < 3)
-		return 0;
-
-	if (header[0] != 0) {
-		debug_printf("Invalid header[0]: %d. Aborting.\n", header[0]);
-		return 0;
-	}
-
-	size = header[1] | (header[2] << 8);
-
-	*buf = g_new0(char, size);
-
-	for (len = 0; len < size; len += read(fd, *buf + len, size - len));
-
-	return len;
-}
-#endif
-
 static int msn_send_im(struct gaim_connection *gc, const char *who, const char *message, int len, int flags)
 {
 	struct msn_data *md = gc->proto_data;
@@ -1625,21 +1458,6 @@
 	}
 }
 
-#if 0
-static void msn_ask_send_file(struct gaim_connection *gc, char *destsn)
-{
-	struct msn_data *md = (struct msn_data *)gc->proto_data;
-	struct msn_file_transfer *mft = g_new0(struct msn_file_transfer, 1);
-
-	mft->type = MFT_SENDFILE_OUT;
-	mft->sn = g_strdup(destsn);
-	mft->gc = gc;
-
-	md->file_transfers = g_slist_append(md->file_transfers, mft);
-
-	mft->xfer = transfer_out_add(gc, mft->sn);
-}
-#endif
 static char *msn_status_text(struct buddy *b) {
 	if (b->uc & UC_UNAVAILABLE)
 		return g_strdup(msn_get_away_text(b->uc >> 1));
@@ -1656,22 +1474,6 @@
 static GList *msn_buddy_menu(struct gaim_connection *gc, const char *who)
 {
 	GList *m = NULL;
-#if 0
-	struct proto_buddy_menu *pbm;
-	struct buddy *b = gaim_find_buddy(gc->account, who);
-	static char buf[MSN_BUF_LEN];
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("Send File");
-	pbm->callback = msn_ask_send_file;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	if (!b || !(b->uc >> 1))
-		return m;
-#endif
-
-
 
 	return m;
 }
@@ -2003,86 +1805,105 @@
 		g_free(b->proto_data);
 }
 
-G_MODULE_EXPORT void msn_init(struct prpl *ret)
+static GaimPluginProtocolInfo prpl_info =
+{
+	GAIM_PROTO_MSN,
+	OPT_PROTO_MAIL_CHECK,
+	NULL,
+	NULL,
+	msn_list_icon,
+	msn_list_emblems,
+	msn_status_text,
+	msn_tooltip_text,
+	msn_away_states,
+	msn_actions,
+	msn_buddy_menu,
+	NULL,
+	msn_login,
+	msn_close,
+	msn_send_im,
+	NULL,
+	msn_send_typing,
+	NULL,
+	msn_set_away,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	msn_set_idle,
+	NULL,
+	msn_add_buddy,
+	NULL,
+	msn_rem_buddy,
+	NULL,
+	msn_add_permit,
+	msn_add_deny,
+	msn_rem_permit,
+	msn_rem_deny,
+	msn_set_permit_deny,
+	NULL,
+	NULL,
+	msn_chat_invite,
+	msn_chat_leave,
+	NULL,
+	msn_chat_send,
+	msn_keepalive,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	msn_buddy_free,
+	msn_convo_closed,
+	msn_normalize
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-msn",                                       /**< id             */
+	"MSN",                                            /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("MSN Protocol Plugin"),
+	                                                  /**  description    */
+	N_("MSN Protocol Plugin"),
+	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
 {
 	struct proto_user_opt *puo;
-	ret->protocol = PROTO_MSN;
-	ret->options = OPT_PROTO_MAIL_CHECK;
-	ret->name = g_strdup("MSN");
-	ret->list_icon = msn_list_icon;
-	ret->list_emblems = msn_list_emblems;
-	ret->buddy_menu = msn_buddy_menu;
-	ret->login = msn_login;
-	ret->close = msn_close;
-	ret->send_im = msn_send_im;
-	ret->send_typing = msn_send_typing;
-	ret->away_states = msn_away_states;
-	ret->status_text = msn_status_text;
-	ret->tooltip_text = msn_tooltip_text;
-	ret->set_away = msn_set_away;
-	ret->set_idle = msn_set_idle;
-	ret->add_buddy = msn_add_buddy;
-	ret->remove_buddy = msn_rem_buddy;
-	ret->chat_send = msn_chat_send;
-	ret->chat_invite = msn_chat_invite;
-	ret->chat_leave = msn_chat_leave;
-	ret->normalize = msn_normalize;
-	ret->actions = msn_actions;
-	ret->convo_closed = msn_convo_closed;
-	ret->keepalive = msn_keepalive;
-	ret->set_permit_deny = msn_set_permit_deny;
-	ret->add_permit = msn_add_permit;
-	ret->rem_permit = msn_rem_permit;
-	ret->add_deny = msn_add_deny;
-	ret->rem_deny = msn_rem_deny;
-	ret->buddy_free = msn_buddy_free;
-
-#if 0
-	ret->file_transfer_cancel = msn_file_transfer_cancel;
-	ret->file_transfer_in = msn_file_transfer_in;
-	ret->file_transfer_out = msn_file_transfer_out;
-	ret->file_transfer_done = msn_file_transfer_done;
-	ret->file_transfer_read = msn_file_transfer_read;
-#endif
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Login Server:"));
 	puo->def = g_strdup(MSN_SERVER);
 	puo->pos = USEROPT_MSNSERVER;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Port:"));
 	puo->def = g_strdup("1863");
 	puo->pos = USEROPT_MSNPORT;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
-
-	my_protocol = ret;
-}
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
 
-#ifndef STATIC
-
-G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
-{
-	msn_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
+	my_protocol = plugin;
 }
 
-G_MODULE_EXPORT void gaim_plugin_remove()
-{
-	struct prpl *p = find_prpl(PROTO_MSN);
-	if (p == my_protocol)
-		unload_protocol(p);
-}
-
-G_MODULE_EXPORT char *name()
-{
-	return "MSN";
-}
-
-G_MODULE_EXPORT char *description()
-{
-	return PRPL_DESC("MSN");
-}
-
-#endif
+GAIM_INIT_PLUGIN(msn, __init_plugin, info);
--- a/src/protocols/napster/napster.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/napster/napster.c	Fri Apr 25 06:47:33 2003 +0000
@@ -563,25 +563,23 @@
 		*se = "offline";
 }
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
+#if 0
 G_MODULE_EXPORT void napster_init(struct prpl *ret)
 {
 	struct proto_user_opt *puo;
 	ret->add_buddies = nap_add_buddies;
 	ret->remove_buddy = nap_remove_buddy;
-	ret->add_permit = NULL;
-	ret->rem_permit = NULL;
-	ret->add_deny = NULL;
-	ret->rem_deny = NULL;
-	ret->warn = NULL;
-	ret->chat_info = nap_chat_info;
-	ret->join_chat = nap_join_chat;
-	ret->chat_invite = NULL;
+//	ret->add_permit = NULL;
+//	ret->rem_permit = NULL;
+//	ret->add_deny = NULL;
+//	ret->rem_deny = NULL;
+//	ret->warn = NULL;
+//	ret->chat_invite = NULL;
 	ret->chat_leave = nap_chat_leave;
-	ret->chat_whisper = NULL;
-	ret->chat_send = nap_chat_send;
-	ret->keepalive = NULL;
+//	ret->chat_whisper = NULL;
+//	ret->keepalive = NULL;
 	ret->protocol = PROTO_NAPSTER;
 	ret->name = g_strdup("Napster");
 	ret->list_icon = nap_list_icon;
@@ -589,29 +587,26 @@
 	ret->login = nap_login;
 	ret->close = nap_close;
 	ret->send_im = nap_send_im;
-	ret->set_info = NULL;
-	ret->get_info = NULL;
-	ret->set_away = NULL;
-	ret->set_dir = NULL;
-	ret->get_dir = NULL;
-	ret->dir_search = NULL;
-	ret->set_idle = NULL;
-	ret->change_passwd = NULL;
+//	ret->set_info = NULL;
+//	ret->get_info = NULL;
+//	ret->set_away = NULL;
+//	ret->set_dir = NULL;
+//	ret->get_dir = NULL;
+//	ret->dir_search = NULL;
+//	ret->set_idle = NULL;
+//	ret->change_passwd = NULL;
 	ret->add_buddy = nap_add_buddy;
-	ret->add_buddies = nap_add_buddies;
-	ret->remove_buddy = nap_remove_buddy;
-	ret->add_permit = NULL;
-	ret->rem_permit = NULL;
-	ret->add_deny = NULL;
-	ret->rem_deny = NULL;
-	ret->warn = NULL;
+//	ret->add_permit = NULL;
+//	ret->rem_permit = NULL;
+//	ret->add_deny = NULL;
+//	ret->rem_deny = NULL;
+//	ret->warn = NULL;
 	ret->chat_info = nap_chat_info;
 	ret->join_chat = nap_join_chat;
-	ret->chat_invite = NULL;
-	ret->chat_leave = nap_chat_leave;
-	ret->chat_whisper = NULL;
+//	ret->chat_invite = NULL;
+//	ret->chat_whisper = NULL;
 	ret->chat_send = nap_chat_send;
-	ret->keepalive = NULL;
+//	ret->keepalive = NULL;
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup(_("Server:"));
@@ -636,3 +631,107 @@
 	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
 }
 #endif
+#endif
+
+static GaimPluginProtocolInfo prpl_info =
+{
+	GAIM_PROTO_NAPSTER,
+	0,
+	NULL,
+	NULL,
+	nap_list_icon,
+	nap_list_emblems,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	nap_chat_info,
+	nap_login,
+	nap_close,
+	nap_send_im,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	nap_add_buddy,
+	nap_add_buddies,
+	nap_remove_buddy,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	nap_join_chat,
+	NULL,
+	nap_chat_leave,
+	NULL,
+	nap_chat_send,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-napster",                                     /**< id             */
+	"NAPSTER",                                        /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("NAPSTER Protocol Plugin"),
+	                                                  /**  description    */
+	N_("NAPSTER Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+	struct proto_user_opt *puo;
+
+	puo = g_new0(struct proto_user_opt, 1);
+	puo->label = g_strdup("Server");
+	puo->def   = g_strdup(NAP_SERVER);
+	puo->pos   = USEROPT_NAPSERVER;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	puo = g_new0(struct proto_user_opt, 1);
+	puo->label = g_strdup("Port:");
+	puo->def   = g_strdup("8888");
+	puo->pos   = USEROPT_NAPPORT;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	my_protocol = plugin;
+}
+
+GAIM_INIT_PLUGIN(napster, __init_plugin, info);
--- a/src/protocols/oscar/oscar.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/oscar/oscar.c	Fri Apr 25 06:47:33 2003 +0000
@@ -68,7 +68,7 @@
 
 #define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
 /* For win32 compatability */
 G_MODULE_IMPORT GSList *connections;
@@ -508,6 +508,7 @@
 		/* oh boy. this is probably bad. i guess the only thing we 
 		 * can really do is return? */
 		debug_printf("oscar callback for closed connection (2).\n");
+		debug_printf("gc = %p\n", gc);
 		return;
 	}
 
@@ -638,6 +639,8 @@
 	struct gaim_connection *gc = new_gaim_conn(account);
 	struct oscar_data *od = gc->proto_data = g_new0(struct oscar_data, 1);
 
+	debug_printf("oscar_login: gc = %p\n", gc);
+
 	if (isdigit(*account->username)) {
 		od->icq = TRUE;
 		gc->password[8] = 0;
@@ -2304,7 +2307,7 @@
 		strncpy(d->ip, args->verifiedip, sizeof(d->ip));
 		memcpy(d->cookie, args->cookie, 8);
 		g_snprintf(buf, sizeof buf, _("%s has just asked to directly connect to %s"), userinfo->sn, gc->username);
-		do_ask_dialog(buf, _("This requires a direct connection between the two computers and is necessary for IM Images.  Because your IP address will be revealed, this may be considered a privacy risk."), d, _("Connect"), accept_direct_im, _("Cancel"), cancel_direct_im, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+		do_ask_dialog(buf, _("This requires a direct connection between the two computers and is necessary for IM Images.  Because your IP address will be revealed, this may be considered a privacy risk."), d, _("Connect"), accept_direct_im, _("Cancel"), cancel_direct_im, my_protocol->handle, FALSE);
 	} else {
 		debug_printf("Unknown reqclass %hu\n", args->reqclass);
 	}
@@ -2364,7 +2367,7 @@
 	data->gc = gc;
 	data->name = g_strdup(name);
 	data->nick = NULL;
-	do_ask_dialog(_("Request Authorization"), dialog_msg, data, _("Request Authorization"), gaim_auth_request_msgprompt, _("Cancel"), gaim_auth_dontrequest, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+	do_ask_dialog(_("Request Authorization"), dialog_msg, data, _("Request Authorization"), gaim_auth_request_msgprompt, _("Cancel"), gaim_auth_dontrequest, my_protocol->handle, FALSE);
 
 	g_free(dialog_msg);
 	g_free(nombre);
@@ -2476,7 +2479,7 @@
 				data->gc = gc;
 				data->name = g_strdup_printf("%lu", args->uin);
 				data->nick = NULL;
-				do_ask_dialog(_("Authorization Request"), dialog_msg, data, _("Authorize"), gaim_auth_grant, _("Deny"), gaim_auth_dontgrant_msgprompt, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+				do_ask_dialog(_("Authorization Request"), dialog_msg, data, _("Authorize"), gaim_auth_grant, _("Deny"), gaim_auth_dontgrant_msgprompt, my_protocol->handle, FALSE);
 				g_free(dialog_msg);
 			}
 		} break;
@@ -2538,7 +2541,7 @@
 					data->gc = gc;
 					data->name = g_strdup(text[i*2+1]);
 					data->nick = g_strdup(text[i*2+2]);
-					do_ask_dialog(message, _("Do you want to add this contact to your Buddy List?"), data, _("Add"), gaim_icq_contactadd, _("Decline"), gaim_free_name_data, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+					do_ask_dialog(message, _("Do you want to add this contact to your Buddy List?"), data, _("Add"), gaim_icq_contactadd, _("Decline"), gaim_free_name_data, my_protocol->handle, FALSE);
 					g_free(message);
 				}
 				g_strfreev(text);
@@ -4840,7 +4843,7 @@
 	data->gc = gc;
 	data->name = g_strdup(sn);
 	data->nick = NULL;
-	do_ask_dialog(_("Authorization Given"), dialog_msg, data, _("Yes"), gaim_icq_contactadd, _("No"), gaim_free_name_data, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+	do_ask_dialog(_("Authorization Given"), dialog_msg, data, _("Yes"), gaim_icq_contactadd, _("No"), gaim_free_name_data, my_protocol->handle, FALSE);
 
 	g_free(dialog_msg);
 	g_free(nombre);
@@ -4874,7 +4877,7 @@
 	data->gc = gc;
 	data->name = g_strdup(sn);
 	data->nick = NULL;
-	do_ask_dialog(_("Authorization Request"), dialog_msg, data, _("Authorize"), gaim_auth_grant, _("Deny"), gaim_auth_dontgrant_msgprompt, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+	do_ask_dialog(_("Authorization Request"), dialog_msg, data, _("Authorize"), gaim_auth_grant, _("Deny"), gaim_auth_dontgrant_msgprompt, my_protocol->handle, FALSE);
 
 	g_free(dialog_msg);
 	g_free(nombre);
@@ -5410,7 +5413,7 @@
 	data->who = g_strdup(who);
 	data->gc = gc;
 	g_snprintf(buf, sizeof(buf),  _("You have selected to open a Direct IM connection with %s."), who);
-	do_ask_dialog(buf, _("Because this reveals your IP address, it may be considered a privacy risk.  Do you wish to continue?"), data, _("Connect"), oscar_direct_im, _("Cancel"), oscar_cancel_direct_im, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+	do_ask_dialog(buf, _("Because this reveals your IP address, it may be considered a privacy risk.  Do you wish to continue?"), data, _("Connect"), oscar_direct_im, _("Cancel"), oscar_cancel_direct_im, my_protocol->handle, FALSE);
 }
 
 static void oscar_set_permit_deny(struct gaim_connection *gc) {
@@ -5823,77 +5826,111 @@
 	g_free(dim);
 }
 
-G_MODULE_EXPORT void oscar_init(struct prpl *ret) {
+static GaimPluginProtocolInfo prpl_info =
+{
+	GAIM_PROTO_OSCAR,
+	OPT_PROTO_MAIL_CHECK | OPT_PROTO_BUDDY_ICON | OPT_PROTO_IM_IMAGE,
+	NULL,
+	NULL,
+	oscar_list_icon,
+	oscar_list_emblems,
+	oscar_status_text,
+	oscar_tooltip_text,
+	oscar_away_states,
+	oscar_actions,
+	oscar_buddy_menu,
+	oscar_chat_info,
+	oscar_login,
+	oscar_close,
+	oscar_send_im,
+	oscar_set_info,
+	oscar_send_typing,
+	oscar_get_info,
+	oscar_set_away,
+	oscar_get_away,
+	oscar_set_dir,
+	NULL,
+	oscar_dir_search,
+	oscar_set_idle,
+	oscar_change_passwd,
+	oscar_add_buddy,
+	oscar_add_buddies,
+	oscar_remove_buddy,
+	oscar_remove_buddies,
+	oscar_add_permit,
+	oscar_add_deny,
+	oscar_rem_permit,
+	oscar_rem_deny,
+	oscar_set_permit_deny,
+	oscar_warn,
+	oscar_join_chat,
+	oscar_chat_invite,
+	oscar_chat_leave,
+	NULL,
+	oscar_chat_send,
+	oscar_keepalive,
+	NULL,
+	NULL,
+	NULL,
+#ifndef NOSSI
+	oscar_alias_buddy,
+	oscar_move_buddy,
+	oscar_rename_group,
+#else
+	NULL,
+	NULL,
+	NULL,
+#endif
+	NULL,
+	oscar_convo_closed,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-oscar",                                     /**< id             */
+	"AIM/ICQ",                                        /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("AIM/ICQ Protocol Plugin"),
+	                                                  /**  description    */
+	N_("AIM/ICQ Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
 	struct proto_user_opt *puo;
-	ret->protocol = PROTO_OSCAR;
-	ret->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_BUDDY_ICON | OPT_PROTO_IM_IMAGE;
-	ret->name = g_strdup("AIM/ICQ");
-	ret->list_icon = oscar_list_icon;
-	ret->list_emblems = oscar_list_emblems;
-	ret->tooltip_text = oscar_tooltip_text;
-	ret->status_text = oscar_status_text;
-	ret->away_states = oscar_away_states;
-	ret->actions = oscar_actions;
-	ret->buddy_menu = oscar_buddy_menu;
-	ret->login = oscar_login;
-	ret->close = oscar_close;
-	ret->send_im = oscar_send_im;
-	ret->send_typing = oscar_send_typing;
-	ret->set_info = oscar_set_info;
-	ret->get_info = oscar_get_info;
-	ret->set_away = oscar_set_away;
-	ret->get_away = oscar_get_away;
-	ret->set_dir = oscar_set_dir;
-	ret->get_dir = NULL; /* Oscar really doesn't have this */
-	ret->dir_search = oscar_dir_search;
-	ret->set_idle = oscar_set_idle;
-	ret->change_passwd = oscar_change_passwd;
-	ret->add_buddy = oscar_add_buddy;
-	ret->add_buddies = oscar_add_buddies;
-	ret->remove_buddy = oscar_remove_buddy;
-	ret->remove_buddies = oscar_remove_buddies;
-#ifndef NOSSI
-	ret->group_buddy = oscar_move_buddy;
-	ret->alias_buddy = oscar_alias_buddy;
-	ret->rename_group = oscar_rename_group;
-#endif
-	ret->add_permit = oscar_add_permit;
-	ret->add_deny = oscar_add_deny;
-	ret->rem_permit = oscar_rem_permit;
-	ret->rem_deny = oscar_rem_deny;
-	ret->set_permit_deny = oscar_set_permit_deny;
-
-	ret->warn = oscar_warn;
-	ret->chat_info = oscar_chat_info;
-	ret->join_chat = oscar_join_chat;
-	ret->chat_invite = oscar_chat_invite;
-	ret->chat_leave = oscar_chat_leave;
-	ret->chat_whisper = NULL;
-	ret->chat_send = oscar_chat_send;
-	ret->keepalive = oscar_keepalive;
-	ret->convo_closed = oscar_convo_closed;
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup("Auth Host:");
-	puo->def = g_strdup("login.oscar.aol.com");
-	puo->pos = USEROPT_AUTH;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
+	puo->def   = g_strdup("login.oscar.aol.com");
+	puo->pos   = USEROPT_AUTH;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
 
 	puo = g_new0(struct proto_user_opt, 1);
 	puo->label = g_strdup("Auth Port:");
-	puo->def = g_strdup("5190");
-	puo->pos = USEROPT_AUTHPORT;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
-
-	my_protocol = ret;
+	puo->def   = g_strdup("5190");
+	puo->pos   = USEROPT_AUTH;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	my_protocol = plugin;
 }
 
-#ifndef STATIC
-
-G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
-{
-	oscar_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
-}
-
-#endif
+GAIM_INIT_PLUGIN(oscar, __init_plugin, info);
--- a/src/protocols/toc/toc.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/toc/toc.c	Fri Apr 25 06:47:33 2003 +0000
@@ -52,7 +52,7 @@
 #include "win32dep.h"
 #endif
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
 /* for win32 compatability */
 G_MODULE_IMPORT GSList *connections;
@@ -1451,71 +1451,6 @@
 	return m;
 }
 
-G_MODULE_EXPORT void toc_init(struct prpl *ret)
-{
-	struct proto_user_opt *puo;
-	ret->protocol = PROTO_TOC;
-	ret->options = OPT_PROTO_CORRECT_TIME;
-	ret->name = g_strdup("TOC");
-	ret->list_icon = toc_list_icon;
-	ret->list_emblems = toc_list_emblems;
-	ret->away_states = toc_away_states;
-	ret->actions = toc_actions;
-	ret->buddy_menu = toc_buddy_menu;
-	ret->login = toc_login;
-	ret->close = toc_close;
-	ret->send_im = toc_send_im;
-	ret->set_info = toc_set_info;
-	ret->get_info = toc_get_info;
-	ret->set_away = toc_set_away;
-	ret->set_dir = toc_set_dir;
-	ret->get_dir = toc_get_dir;
-	ret->dir_search = toc_dir_search;
-	ret->set_idle = toc_set_idle;
-	ret->change_passwd = toc_change_passwd;
-	ret->add_buddy = toc_add_buddy;
-	ret->add_buddies = toc_add_buddies;
-	ret->remove_buddy = toc_remove_buddy;
-	ret->remove_buddies = toc_remove_buddies;
-	ret->add_permit = toc_add_permit;
-	ret->add_deny = toc_add_deny;
-	ret->rem_permit = toc_rem_permit;
-	ret->rem_deny = toc_rem_deny;
-	ret->set_permit_deny = toc_set_permit_deny;
-	ret->warn = toc_warn;
-	ret->chat_info = toc_chat_info;
-	ret->join_chat = toc_join_chat;
-	ret->chat_invite = toc_chat_invite;
-	ret->chat_leave = toc_chat_leave;
-	ret->chat_whisper = toc_chat_whisper;
-	ret->chat_send = toc_chat_send;
-	ret->keepalive = toc_keepalive;
-
-	puo = g_new0(struct proto_user_opt, 1);
-	puo->label = g_strdup(_("TOC Host:"));
-	puo->def = g_strdup("toc.oscar.aol.com");
-	puo->pos = USEROPT_AUTH;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
-
-	puo = g_new0(struct proto_user_opt, 1);
-	puo->label = g_strdup(_("TOC Port:"));
-	puo->def = g_strdup("9898");
-	puo->pos = USEROPT_AUTHPORT;
-	ret->user_opts = g_list_append(ret->user_opts, puo);
-
-	my_protocol = ret;
-}
-
-#ifndef STATIC
-
-G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
-{
-	toc_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
-}
-
-#endif
-
 /*********
  * RVOUS ACTIONS
  ********/
@@ -2038,5 +1973,108 @@
 	} else {
 		g_snprintf(buf, sizeof(buf), _("%s requests you to send them a file"), ft->user);
 	}
-	do_ask_dialog(buf, NULL, ft, _("Accept"), toc_accept_ft, _("Cancel"), toc_reject_ft, my_protocol->plug ? my_protocol->plug->handle : NULL, FALSE);
+	do_ask_dialog(buf, NULL, ft, _("Accept"), toc_accept_ft, _("Cancel"), toc_reject_ft, my_protocol->handle, FALSE);
 }
+
+static GaimPluginProtocolInfo prpl_info =
+{
+	GAIM_PROTO_TOC,
+	OPT_PROTO_CORRECT_TIME,
+	NULL,
+	NULL,
+	toc_list_icon,
+	toc_list_emblems,
+	NULL,
+	NULL,
+	toc_away_states,
+	toc_actions,
+	toc_buddy_menu,
+	toc_chat_info,
+	toc_login,
+	toc_close,
+	toc_send_im,
+	toc_set_info,
+	NULL,
+	toc_get_info,
+	toc_set_away,
+	NULL,
+	toc_set_dir,
+	toc_get_dir,
+	toc_dir_search,
+	toc_set_idle,
+	toc_change_passwd,
+	toc_add_buddy,
+	toc_add_buddies,
+	toc_remove_buddy,
+	toc_remove_buddies,
+	toc_add_permit,
+	toc_add_deny,
+	toc_rem_permit,
+	toc_rem_deny,
+	toc_set_permit_deny,
+	toc_warn,
+	toc_join_chat,
+	toc_chat_invite,
+	toc_chat_leave,
+	toc_chat_whisper,
+	toc_chat_send,
+	toc_keepalive,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-toc",                                       /**< id             */
+	"TOC",                                            /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("TOC Protocol Plugin"),
+	                                                  /**  description    */
+	N_("TOC Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+	struct proto_user_opt *puo;
+
+	puo = g_new0(struct proto_user_opt, 1);
+	puo->label = g_strdup(_("TOC Host:"));
+	puo->def = g_strdup("toc.oscar.aol.com");
+	puo->pos = USEROPT_AUTH;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	puo = g_new0(struct proto_user_opt, 1);
+	puo->label = g_strdup(_("TOC Port:"));
+	puo->def = g_strdup("9898");
+	puo->pos = USEROPT_AUTHPORT;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	my_protocol = plugin;
+}
+
+GAIM_INIT_PLUGIN(toc, __init_plugin, info);
--- a/src/protocols/yahoo/yahoo.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/yahoo/yahoo.c	Fri Apr 25 06:47:33 2003 +0000
@@ -1381,9 +1381,10 @@
 	yahoo_packet_free(pkt);
 }
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
-G_MODULE_EXPORT void yahoo_init(struct prpl *ret) {
+#if 0
+G_MODULE_EXPORT void yahoo_init(GaimPlugin *ret) {
 	struct proto_user_opt *puo;
 	ret->protocol = PROTO_YAHOO;
 	ret->options = OPT_PROTO_MAIL_CHECK;
@@ -1429,3 +1430,107 @@
 }
 
 #endif
+#endif
+
+static GaimPluginProtocolInfo prpl_info =
+{
+	GAIM_PROTO_YAHOO,
+	OPT_PROTO_MAIL_CHECK,
+	NULL,
+	NULL,
+	yahoo_list_icon,
+	yahoo_list_emblems,
+	yahoo_status_text,
+	yahoo_tooltip_text,
+	yahoo_away_states,
+	yahoo_actions,
+	yahoo_buddy_menu,
+	NULL,
+	yahoo_login,
+	yahoo_close,
+	yahoo_send_im,
+	NULL,
+	yahoo_send_typing,
+	NULL,
+	yahoo_set_away,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	yahoo_set_idle,
+	NULL,
+	yahoo_add_buddy,
+	NULL,
+	yahoo_remove_buddy,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	yahoo_keepalive,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-yahoo",                                     /**< id             */
+	"Yahoo",	                                      /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Yahoo Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Yahoo Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+	struct proto_user_opt *puo;
+
+	puo = g_new0(struct proto_user_opt, 1);
+	puo->label = g_strdup("Pager Host:");
+	puo->def   = g_strdup(YAHOO_PAGER_HOST);
+	puo->pos   = USEROPT_PAGERHOST;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	puo = g_new0(struct proto_user_opt, 1);
+	puo->label = g_strdup("Pager Port:");
+	puo->def   = g_strdup("5050");
+	puo->pos   = USEROPT_PAGERPORT;
+	prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo);
+
+	my_protocol = plugin;
+}
+
+GAIM_INIT_PLUGIN(yahoo, __init_plugin, info);
--- a/src/protocols/zephyr/zephyr.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/protocols/zephyr/zephyr.c	Fri Apr 25 06:47:33 2003 +0000
@@ -950,39 +950,93 @@
 	return "zephyr";
 }
 
-static struct prpl *my_protocol = NULL;
+static GaimPlugin *my_protocol = NULL;
 
-void zephyr_init(struct prpl *ret)
+static GaimPluginProtocolInfo prpl_info =
 {
-	ret->protocol = PROTO_ZEPHYR;
-	ret->options = OPT_PROTO_NO_PASSWORD;
-	ret->name = g_strdup("Zephyr");
-	ret->list_icon = zephyr_list_icon;
-	ret->login = zephyr_login;
-	ret->close = zephyr_close;
-	ret->add_buddy = zephyr_add_buddy;
-	ret->remove_buddy = zephyr_remove_buddy;
-	ret->send_im = zephyr_send_im;
-	ret->get_info = zephyr_zloc;
-	ret->normalize = zephyr_normalize;
-	ret->buddy_menu = zephyr_buddy_menu;
-	ret->away_states = zephyr_away_states;
-	ret->set_away = zephyr_set_away;
-	ret->chat_info = zephyr_chat_info;
-	ret->join_chat = zephyr_join_chat;
-	ret->chat_send = zephyr_chat_send;
-	ret->chat_leave = zephyr_chat_leave;
+	GAIM_PROTO_ZEPHYR,
+	OPT_PROTO_NO_PASSWORD,
+	NULL,
+	NULL,
+	zephyr_list_icon,
+	NULL,
+	NULL,
+	NULL,
+	zephyr_away_states,
+	NULL,
+	zephyr_buddy_menu,
+	zephyr_chat_info,
+	zephyr_login,
+	zephyr_close,
+	zephyr_send_im,
+	NULL,
+	NULL,
+	zephyr_zloc,
+	zephyr_set_away,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	zephyr_add_buddy,
+	NULL,
+	zephyr_remove_buddy,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	zephyr_join_chat,
+	NULL,
+	zephyr_chat_leave,
+	NULL,
+	zephyr_chat_send,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	zephyr_normalize
+};
 
-	my_protocol = ret;
+static GaimPluginInfo info =
+{
+	2,                                                /**< api_version    */
+	GAIM_PLUGIN_PROTOCOL,                             /**< type           */
+	NULL,                                             /**< ui_requirement */
+	0,                                                /**< flags          */
+	NULL,                                             /**< dependencies   */
+	GAIM_PRIORITY_DEFAULT,                            /**< priority       */
+
+	"prpl-zephyr",                                     /**< id             */
+	"Zephyr",                                        /**< name           */
+	VERSION,                                          /**< version        */
+	                                                  /**  summary        */
+	N_("Zephyr Protocol Plugin"),
+	                                                  /**  description    */
+	N_("Zephyr Protocol Plugin"),
+	NULL,                                             /**< author         */
+	WEBSITE,                                          /**< homepage       */
+
+	NULL,                                             /**< load           */
+	NULL,                                             /**< unload         */
+	NULL,                                             /**< destroy        */
+
+	NULL,                                             /**< ui_info        */
+	&prpl_info                                        /**< extra_info     */
+};
+
+static void
+__init_plugin(GaimPlugin *plugin)
+{
+	my_protocol = plugin;
 }
 
-#ifndef STATIC
-
-G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
-{
-	zephyr_init(prpl);
-	prpl->plug->desc.api_version = PLUGIN_API_VERSION;
-}
-
-
-#endif
+GAIM_INIT_PLUGIN(zephyr, __init_plugin, info);
--- a/src/prpl.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/prpl.c	Fri Apr 25 06:47:33 2003 +0000
@@ -36,7 +36,6 @@
 GSList *protocols = NULL;
 
 GtkWidget *protomenu = NULL;
-int prpl_accounts[PROTO_UNTAKEN];
 
 struct _prompt {
 	GtkWidget *window;
@@ -46,101 +45,26 @@
 	void *data;
 };
 
-struct prpl *find_prpl(GaimProtocol type)
+GaimPlugin *
+gaim_find_prpl(GaimProtocol type)
 {
-	GSList *e = protocols;
-	struct prpl *r;
+	GSList *l;
+	GaimPlugin *plugin;
 
-	while (e) {
-		r = (struct prpl *)e->data;
-		if (r->protocol == type)
-			return r;
-		e = e->next;
+	for (l = protocols; l != NULL; l = l->next) {
+		plugin = (GaimPlugin *)l->data;
+
+		/* Just In Case (TM) */
+		if (GAIM_IS_PROTOCOL_PLUGIN(plugin)) {
+
+			if (GAIM_PLUGIN_PROTOCOL_INFO(plugin)->protocol == type)
+				return plugin;
+		}
 	}
 
 	return NULL;
 }
 
-gint proto_compare(struct prpl *a, struct prpl *b)
-{
-	/* neg if a before b, 0 if equal, pos if a after b */
-	return a->protocol - b->protocol;
-}
-
-#ifdef GAIM_PLUGINS
-gboolean load_prpl(struct prpl *p)
-{
-	char *(*gaim_prpl_init)(struct prpl *);
-	debug_printf("Loading protocol %d\n", p->protocol);
-
-	if (!p->plug)
-		return TRUE;
-	
-	p->plug->handle = g_module_open(p->plug->path, 0);
-	if (!p->plug->handle) {
-		debug_printf("%s is unloadable: %s\n", p->plug->path, g_module_error());
-		return TRUE;
-	}
-
-	if (!g_module_symbol(p->plug->handle, "gaim_prpl_init", (gpointer *)&gaim_prpl_init)) {
-		return TRUE;
-	}
-
-	gaim_prpl_init(p);
-	return FALSE;
-}
-#endif
-
-/* This is used only by static protocols */
-void load_protocol(proto_init pi)
-{
-	struct prpl *p = g_new0(struct prpl, 1);
-
-	if (p->protocol == PROTO_ICQ) 
-		do_error_dialog(_("ICQ Protocol detected."),
-				_("Gaim has loaded the ICQ plugin.  This plugin has been deprecated. "
-				  "As such, it was probably not compiled from the same version of the "
-				  "source as this application was, and cannot be guaranteed to work.  "
-				  "It is recommended that you use the AIM/ICQ protocol to connect to ICQ"),
-				GAIM_WARNING);
-	pi(p);
-	protocols = g_slist_insert_sorted(protocols, p, (GCompareFunc)proto_compare);
-	regenerate_user_list();
-}
-
-void unload_protocol(struct prpl *p)
-{
-	GList *c;
-	struct proto_user_split *pus;
-	struct proto_user_opt *puo;
-	if (p->name)
-		g_free(p->name);
-
-	c = p->user_splits;
-	while (c) {
-		pus = c->data;
-		g_free(pus->label);
-		g_free(pus->def);
-		g_free(pus);
-		c = c->next;
-	}
-	g_list_free(p->user_splits);
-	p->user_splits = NULL;
-
-	c = p->user_opts;
-	while (c) {
-		puo = c->data;
-		g_free(puo->label);
-		g_free(puo->def);
-		g_free(puo);
-		c = c->next;
-	}
-	g_list_free(p->user_opts);
-	p->user_opts = NULL;
-}
-
-STATIC_PROTO_INIT
-
 static void des_win(GtkWidget *a, GtkWidget *b)
 {
 	gtk_widget_destroy(b);
@@ -156,7 +80,7 @@
 	gpointer data;
 };
 
-void do_ask_cancel_by_handle(GModule *handle)
+void do_ask_cancel_by_handle(void *handle)
 {
 	GSList *d = do_ask_dialogs;
 
@@ -335,6 +259,7 @@
 {
 	GtkWidget *menuitem;
 	GtkWidget *submenu;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	GList *l;
 	GSList *c = connections;
 	struct proto_actions_menu *pam;
@@ -357,8 +282,12 @@
 
 	while (c) {
 		gc = c->data;
-		if (gc->prpl->actions && gc->login_time)
+
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+		if (prpl_info->actions && gc->login_time)
 			count++;
+
 		c = g_slist_next(c);
 	}
 	c = connections;
@@ -375,12 +304,16 @@
 		GList *act;
 		while (c) {
 			gc = c->data;
-			if (gc->prpl->actions && gc->login_time)
+
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+			if (prpl_info->actions && gc->login_time)
 				break;
+
 			c = g_slist_next(c);
 		}
 
-		act = gc->prpl->actions(gc);
+		act = prpl_info->actions(gc);
 
 		while (act) {
 			if (act->data) {
@@ -403,12 +336,16 @@
 			GtkWidget *image;
 
 			gc = c->data;
-			if (!gc->prpl->actions || !gc->login_time) {
+
+			prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+			if (!prpl_info->actions || !gc->login_time) {
 				c = g_slist_next(c);
 				continue;
 			}
 
-			g_snprintf(buf, sizeof(buf), "%s (%s)", gc->username, gc->prpl->name);
+			g_snprintf(buf, sizeof(buf), "%s (%s)",
+					   gc->username, gc->prpl->info->name);
 			menuitem = gtk_image_menu_item_new_with_label(buf);
 
 			pixbuf = create_prpl_icon(gc->account);
@@ -429,7 +366,7 @@
 			gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
 			gtk_widget_show(submenu);
 
-			act = gc->prpl->actions(gc);
+			act = prpl_info->actions(gc);
 
 			while (act) {
 				if (act->data) {
@@ -782,9 +719,11 @@
 				     ((GtkBoxChild *)GTK_BOX(reg_area)->children->data)->widget);
 
 	while (P) {
-		struct prpl *p = P->data;
-		if (p->register_user)
+		GaimPlugin *p = P->data;
+
+		if (GAIM_PLUGIN_PROTOCOL_INFO(p)->register_user)
 			break;
+
 		P = P->next;
 	}
 
@@ -802,10 +741,10 @@
 	gtk_widget_set_sensitive(reg_reg, TRUE);
 
 	while (P) {	/* we can safely ignore all the previous ones */
-		struct prpl *p = P->data;
+		GaimPlugin *p = P->data;
 		P = P->next;
 
-		if (!p->register_user)
+		if (GAIM_PLUGIN_PROTOCOL_INFO(p)->register_user)
 			continue;
 
 		/* do stuff */
@@ -860,38 +799,3 @@
 	gtk_widget_show_all(regdlg);
 }
 
-static gboolean delayed_unload(void *handle) {
-	g_module_close(handle);
-	return FALSE;
-}
-
-gboolean ref_protocol(struct prpl *p) {
-#ifdef GAIM_PLUGINS
-	if(p->plug) { /* This protocol is a plugin */
-		prpl_accounts[p->protocol]++;
-		debug_printf("Protocol %s refcount now %d\n", p->name, prpl_accounts[p->protocol]);
-		if(!p->plug->handle) { /*But the protocol isn't yet loaded */
-			unload_protocol(p);
-			if (load_prpl(p))
-				return FALSE;
-		}
-	}
-#endif /* GAIM_PLUGINS */
-	return TRUE;
-}
-
-void unref_protocol(struct prpl *p) {
-#ifdef GAIM_PLUGINS
-	if(p->plug) { /* This protocol is a plugin */
-		prpl_accounts[p->protocol]--;
-		debug_printf("Protocol %s refcount now %d\n", p->name, prpl_accounts[p->protocol]);
-		if(prpl_accounts[p->protocol] == 0) { /* No longer needed */
-			debug_printf("Throwing out %s protocol plugin\n", p->name);
-			do_ask_cancel_by_handle(p->plug->handle);
-			g_timeout_add(0, delayed_unload, p->plug->handle);
-			p->plug->handle = NULL;
-		}
-	}
-#endif /* GAIM_PLUGINS */
-}
-
--- a/src/prpl.h	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/prpl.h	Fri Apr 25 06:47:33 2003 +0000
@@ -27,6 +27,8 @@
 #ifndef _GAIM_PRPL_H_
 #define _GAIM_PRPL_H_
 
+typedef struct _GaimPluginProtocolInfo GaimPluginProtocolInfo;
+
 #include "core.h"
 #include "proxy.h"
 #include "multi.h"
@@ -44,30 +46,30 @@
  */
 typedef enum
 {
-	PROTO_TOC = 0,     /**< AIM TOC protocol          */
-	PROTO_OSCAR,       /**< AIM OSCAR protocol        */
-	PROTO_YAHOO,       /**< Yahoo Messenger protocol  */
-	PROTO_ICQ,         /**< Outdated ICQ protocol     */
-	PROTO_MSN,         /**< MSN Messenger protocol    */
-	PROTO_IRC,         /**< IRC protocol              */
-	PROTO_FTP,         /**< FTP protocol              */
-	PROTO_VGATE,       /**< VGATE protocol            */
-	PROTO_JABBER,      /**< Jabber protocol           */
-	PROTO_NAPSTER,     /**< Napster/OpenNAP protocol  */
-	PROTO_ZEPHYR,      /**< MIT Zephyr protocol       */
-	PROTO_GADUGADU,    /**< Gadu-Gadu protocol        */
-	PROTO_SAMETIME,    /**< SameTime protocol         */
-	PROTO_TLEN,        /**< TLEN protocol             */
-	PROTO_RVP,         /**< RVP protocol              */
-	PROTO_BACKRUB,     /**< Instant Massager protocol */
-	PROTO_MOO,         /**< MOO protocol              */
-	PROTO_UNTAKEN      /**< Untaken protocol number   */
+	GAIM_PROTO_TOC = 0,     /**< AIM TOC protocol          */
+	GAIM_PROTO_OSCAR,       /**< AIM OSCAR protocol        */
+	GAIM_PROTO_YAHOO,       /**< Yahoo Messenger protocol  */
+	GAIM_PROTO_ICQ,         /**< Outdated ICQ protocol     */
+	GAIM_PROTO_MSN,         /**< MSN Messenger protocol    */
+	GAIM_PROTO_IRC,         /**< IRC protocol              */
+	GAIM_PROTO_FTP,         /**< FTP protocol              */
+	GAIM_PROTO_VGATE,       /**< VGATE protocol            */
+	GAIM_PROTO_JABBER,      /**< Jabber protocol           */
+	GAIM_PROTO_NAPSTER,     /**< Napster/OpenNAP protocol  */
+	GAIM_PROTO_ZEPHYR,      /**< MIT Zephyr protocol       */
+	GAIM_PROTO_GADUGADU,    /**< Gadu-Gadu protocol        */
+	GAIM_PROTO_SAMETIME,    /**< SameTime protocol         */
+	GAIM_PROTO_TLEN,        /**< TLEN protocol             */
+	GAIM_PROTO_RVP,         /**< RVP protocol              */
+	GAIM_PROTO_BACKRUB,     /**< Instant Massager protocol */
+	GAIM_PROTO_MOO,         /**< MOO protocol              */
+	GAIM_PROTO_UNTAKEN      /**< Untaken protocol number   */
 
 } GaimProtocol;
 
 /** Default protocol plugin description */
-#define PRPL_DESC(x) \
-		"Allows gaim to use the " x " protocol.\n\n"        \
+#define GAIM_PRPL_DESC(x) \
+		"Allows gaim to use the " (x) " protocol.\n\n"      \
 		"Now that you have loaded this protocol, use the "  \
 		"Account Editor to add an account that uses this "  \
 		"protocol. You can access the Account Editor from " \
@@ -75,7 +77,7 @@
 		"in the \"Tools\" menu in the buddy list window."
 
 /** Default protocol */
-#define DEFAULT_PROTO   PROTO_OSCAR
+#define GAIM_PROTO_DEFAULT GAIM_PROTO_OSCAR
 
 /*@}*/
 
@@ -163,54 +165,50 @@
 #define GAIM_AWAY_CUSTOM _("Custom")
 
 /**
- * Protocol plugin initialization function.
- */
-typedef void (*proto_init)(struct prpl *);
-
-/**
- * A protocol plugin structure.
+ * A protocol plugin information structure.
  *
  * Every protocol plugin initializes this structure. It is the gateway
  * between gaim and the protocol plugin.
  */
-struct prpl
+struct _GaimPluginProtocolInfo
 {
 	GaimProtocol protocol;        /**< The protocol type.         */
 	GaimProtocolOptions options;  /**< Protocol options.          */
-	struct gaim_plugin *plug;     /**< The base plugin structure. */
-	char *name;
+
+	/* user_splits is a GList of g_malloc'd struct proto_user_split */
+	GList *user_splits;
+	/* user_opts is a GList* of g_malloc'd struct proto_user_opts */
+	GList *user_opts;
 
 	/** 
 	 * Returns the base icon name for the given buddy and account.  
 	 * If buddy is NULL, it will return the name to use for the account's icon
 	 */
-	const char *(* list_icon)(struct gaim_account *account, struct buddy *buddy);
+	const char *(*list_icon)(struct gaim_account *account, struct buddy *buddy);
 
 	/**
-	 * Fills the four char**'s with string identifiers for "emblems" that the UI will
-	 * interpret and display as relevant
+	 * Fills the four char**'s with string identifiers for "emblems"
+	 * that the UI will interpret and display as relevant
 	 */
-	void (* list_emblems)(struct buddy *buddy, char **se, char **sw, char **nw, char **ne);
+	void (*list_emblems)(struct buddy *buddy, char **se, char **sw,
+						  char **nw, char **ne);
 
 	/**
-	 * Gets a short string representing this buddy's status.  This will be shown
-	 * on the buddy list.
+	 * Gets a short string representing this buddy's status.  This will
+	 * be shown on the buddy list.
 	 */
-	char *(* status_text)(struct buddy *buddy);
+	char *(*status_text)(struct buddy *buddy);
 	
 	/**
 	 * Gets a string to put in the buddy list tooltip.
 	 */
-	char *(* tooltip_text)(struct buddy *buddy);
+	char *(*tooltip_text)(struct buddy *buddy);
 	
-	GList *(* away_states)(struct gaim_connection *gc);
-	GList *(* actions)(struct gaim_connection *gc);
-	/* user_splits is a GList of g_malloc'd struct proto_user_split */
-	GList *user_splits;
-	/* user_opts is a GList* of g_malloc'd struct proto_user_opts */
-	GList *user_opts;
-	GList *(* buddy_menu)(struct gaim_connection *, const char *);
-	GList *(* chat_info)(struct gaim_connection *);
+	GList *(*away_states)(struct gaim_connection *gc);
+	GList *(*actions)(struct gaim_connection *gc);
+
+	GList *(*buddy_menu)(struct gaim_connection *, const char *);
+	GList *(*chat_info)(struct gaim_connection *);
 
 	/* All the server-related functions */
 
@@ -221,118 +219,84 @@
 	 * can set your dir info, the ui shows a dialog and needs to call
 	 * set_dir in order to set it)
 	 */
-	void (* login)		(struct gaim_account *);
-	void (* close)		(struct gaim_connection *);
-	int  (* send_im)	(struct gaim_connection *, const char *who, const char *message,
-						 int len, int away);
-	void (* set_info)	(struct gaim_connection *, char *info);
-	int  (* send_typing)    (struct gaim_connection *, char *name, int typing);
-	void (* get_info)	(struct gaim_connection *, const char *who);
-	void (* set_away)	(struct gaim_connection *, char *state, char *message);
-	void (* get_away)       (struct gaim_connection *, const char *who);
-	void (* set_dir)	(struct gaim_connection *, const char *first,
-							   const char *middle,
-							   const char *last,
-							   const char *maiden,
-							   const char *city,
-							   const char *state,
-							   const char *country,
-							   int web);
-	void (* get_dir)	(struct gaim_connection *, const char *who);
-	void (* dir_search)	(struct gaim_connection *, const char *first,
-							   const char *middle,
-							   const char *last,
-							   const char *maiden,
-							   const char *city,
-							   const char *state,
-							   const char *country,
-							   const char *email);
-	void (* set_idle)	(struct gaim_connection *, int idletime);
-	void (* change_passwd)	(struct gaim_connection *, const char *old,
-							 const char *new);
-	void (* add_buddy)	(struct gaim_connection *, const char *name);
-	void (* add_buddies)	(struct gaim_connection *, GList *buddies);
-	void (* remove_buddy)	(struct gaim_connection *, char *name, char *group);
-	void (* remove_buddies)	(struct gaim_connection *, GList *buddies,
-							 const char *group);
-	void (* add_permit)	(struct gaim_connection *, const char *name);
-	void (* add_deny)	(struct gaim_connection *, const char *name);
-	void (* rem_permit)	(struct gaim_connection *, const char *name);
-	void (* rem_deny)	(struct gaim_connection *, const char *name);
-	void (* set_permit_deny)(struct gaim_connection *);
-	void (* warn)		(struct gaim_connection *, char *who, int anonymous);
-	void (* join_chat)	(struct gaim_connection *, GList *data);
-	void (* chat_invite)	(struct gaim_connection *, int id,
-							 const char *who, const char *message);
-	void (* chat_leave)	(struct gaim_connection *, int id);
-	void (* chat_whisper)	(struct gaim_connection *, int id,
-							 char *who, char *message);
-	int  (* chat_send)	(struct gaim_connection *, int id, char *message);
-	void (* keepalive)	(struct gaim_connection *);
+	void (*login)(struct gaim_account *);
+	void (*close)(struct gaim_connection *);
+	int  (*send_im)(struct gaim_connection *, const char *who,
+					const char *message, int len, int away);
+	void (*set_info)(struct gaim_connection *, char *info);
+	int  (*send_typing)(struct gaim_connection *, char *name, int typing);
+	void (*get_info)(struct gaim_connection *, const char *who);
+	void (*set_away)(struct gaim_connection *, char *state, char *message);
+	void (*get_away)(struct gaim_connection *, const char *who);
+	void (*set_dir)(struct gaim_connection *, const char *first,
+					const char *middle, const char *last,
+					const char *maiden, const char *city,
+					const char *state, const char *country, int web);
+	void (*get_dir)(struct gaim_connection *, const char *who);
+	void (*dir_search)(struct gaim_connection *, const char *first,
+					   const char *middle, const char *last,
+					   const char *maiden, const char *city,
+					   const char *state, const char *country,
+					   const char *email);
+	void (*set_idle)(struct gaim_connection *, int idletime);
+	void (*change_passwd)(struct gaim_connection *, const char *old,
+						  const char *new);
+	void (*add_buddy)(struct gaim_connection *, const char *name);
+	void (*add_buddies)(struct gaim_connection *, GList *buddies);
+	void (*remove_buddy)(struct gaim_connection *, char *name, char *group);
+	void (*remove_buddies)(struct gaim_connection *, GList *buddies,
+						   const char *group);
+	void (*add_permit)(struct gaim_connection *, const char *name);
+	void (*add_deny)(struct gaim_connection *, const char *name);
+	void (*rem_permit)(struct gaim_connection *, const char *name);
+	void (*rem_deny)(struct gaim_connection *, const char *name);
+	void (*set_permit_deny)(struct gaim_connection *);
+	void (*warn)(struct gaim_connection *, char *who, int anonymous);
+	void (*join_chat)(struct gaim_connection *, GList *data);
+	void (*chat_invite)(struct gaim_connection *, int id,
+						const char *who, const char *message);
+	void (*chat_leave)(struct gaim_connection *, int id);
+	void (*chat_whisper)(struct gaim_connection *, int id,
+						 char *who, char *message);
+	int  (*chat_send)(struct gaim_connection *, int id, char *message);
+	void (*keepalive)(struct gaim_connection *);
 
 	/* new user registration */
-	void (* register_user)	(struct gaim_account *);
+	void (*register_user)(struct gaim_account *);
 
 	/* get "chat buddy" info and away message */
-	void (* get_cb_info)	(struct gaim_connection *, int, char *who);
-	void (* get_cb_away)	(struct gaim_connection *, int, char *who);
+	void (*get_cb_info)(struct gaim_connection *, int, char *who);
+	void (*get_cb_away)(struct gaim_connection *, int, char *who);
 
 	/* save/store buddy's alias on server list/roster */
-	void (* alias_buddy)	(struct gaim_connection *, const char *who,
-							 const char *alias);
+	void (*alias_buddy)(struct gaim_connection *, const char *who,
+						const char *alias);
 
 	/* change a buddy's group on a server list/roster */
-	void (* group_buddy)	(struct gaim_connection *, const char *who,
-							 const char *old_group, const char *new_group);
+	void (*group_buddy)(struct gaim_connection *, const char *who,
+						const char *old_group, const char *new_group);
 
 	/* rename a group on a server list/roster */
-	void (* rename_group)	(struct gaim_connection *, const char *old_group,
-							 const char *new_group, GList *members);
+	void (*rename_group)(struct gaim_connection *, const char *old_group,
+						 const char *new_group, GList *members);
 
-	void (* buddy_free)	(struct buddy *);
+	void (*buddy_free)(struct buddy *);
 
 	/* this is really bad. */
-	void (* convo_closed)   (struct gaim_connection *, char *who);
+	void (*convo_closed)(struct gaim_connection *, char *who);
+
+	char *(*normalize)(const char *);
+};
 
-	char *(* normalize)(const char *);
-};
+#define GAIM_IS_PROTOCOL_PLUGIN(plugin) \
+	((plugin)->info->type == GAIM_PLUGIN_PROTOCOL)
+
+#define GAIM_PLUGIN_PROTOCOL_INFO(plugin) \
+	((GaimPluginProtocolInfo *)(plugin)->info->extra_info)
 
 /** A list of all loaded protocol plugins. */
 extern GSList *protocols;
 
-/** The number of accounts using a given protocol plugin. */
-extern int prpl_accounts[];
-
-/**
- * Initializes static protocols.
- *
- * This is mostly just for main.c, when it initializes the protocols.
- */
-void static_proto_init();
-
-/**
- * Loads a protocol plugin.
- *
- * @param prpl The initial protocol plugin structure.
- * 
- * @return @c TRUE if the protocol was loaded, or @c FALSE otherwise.
- */
-gboolean load_prpl(struct prpl *prpl);
-
-/**
- * Loads a statically compiled protocol plugin.
- *
- * @param pi The initialization function.
- */
-void load_protocol(proto_init pi);
-
-/**
- * Unloads a protocol plugin.
- *
- * @param prpl The protocol plugin to unload.
- */
-extern void unload_protocol(struct prpl *prpl);
-
 /**
  * Compares two protocol plugins, based off their protocol plugin number.
  *
@@ -343,14 +307,14 @@
  *         0 if the first plugin's number is equal to the second; or
  *         >= 1 if the first plugin's number is greater than the second.
  */
-gint proto_compare(struct prpl *a, struct prpl *b);
+gint gaim_prpl_compare(GaimPlugin *a, GaimPlugin *b);
 
 /**
  * Finds a protocol plugin structure of the specified type.
  *
- * @param type The protocol plugin type.
+ * @param type The protocol plugin;
  */
-struct prpl *find_prpl(GaimProtocol type);
+GaimPlugin *gaim_find_prpl(GaimProtocol type);
 
 /**
  * Creates a menu of all protocol plugins and their protocol-specific
@@ -359,7 +323,7 @@
  * @note This should be UI-specific code, or rewritten in such a way as to
  *       not use any any GTK code.
  */
-void do_proto_menu();
+void do_proto_menu(void);
 
 /**
  * Shows a message saying that somebody added you as a buddy, and asks
@@ -377,9 +341,9 @@
 /**
  * Closes do_ask_dialogs when the associated plugin is unloaded.
  *
- * @param module The module.
+ * @param handle The handle.
  */
-void do_ask_cancel_by_handle(GModule *module);
+void do_ask_cancel_by_handle(void *handle);
 
 /**
  * Asks the user a question and acts on the response.
@@ -448,22 +412,4 @@
  */
 void *get_icon_data(struct gaim_connection *gc, const char *who, int *len);
 
-/* stuff to load/unload PRPLs as necessary */
-
-/**
- * Increments the reference count on a protocol plugin.
- *
- * @param prpl The protocol plugin.
- *
- * @return @c TRUE if the protocol plugin is loaded, or @c FALSE otherwise.
- */
-gboolean ref_protocol(struct prpl *prpl);
-
-/**
- * Decrements the reference count on a protocol plugin. 
- *
- * When the reference count hits 0, the protocol plugin is unloaded.
- */
-void unref_protocol(struct prpl *prpl);
-
 #endif /* _PRPL_H_ */
--- a/src/server.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/server.c	Fri Apr 25 06:47:33 2003 +0000
@@ -40,35 +40,42 @@
 
 void serv_login(struct gaim_account *account)
 {
-	struct prpl *p = find_prpl(account->protocol);
+	GaimPlugin *p = gaim_find_prpl(account->protocol);
+	GaimPluginProtocolInfo *prpl_info = NULL;
 
 	if (account->gc != NULL || p == NULL)
 		return;
 
-	if(!ref_protocol(p))
-		return;
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
 
-	if (p->login) {
-		if (!strlen(account->password) && !(p->options & OPT_PROTO_NO_PASSWORD) &&
-			!(p->options & OPT_PROTO_PASSWORD_OPTIONAL)) {
+	if (prpl_info->login) {
+		if (!strlen(account->password) && !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
+			!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) {
 			do_error_dialog(_("Please enter your password"), NULL, GAIM_ERROR);
 			return;
 		}
 
-		debug_printf(PACKAGE " " VERSION " logging in %s using %s\n", account->username, p->name);
+		debug_printf(PACKAGE " " VERSION " logging in %s using %s\n",
+					 account->username, p->info->name);
 		account->connecting = TRUE;
 		connecting_count++;
 		debug_printf("connecting_count: %d\n", connecting_count);
-		plugin_event(event_connecting, account);
-		p->login(account);
+		gaim_event_broadcast(event_connecting, account);
+		prpl_info->login(account);
 	}
 }
 
 static gboolean send_keepalive(gpointer d)
 {
 	struct gaim_connection *gc = d;
-	if (gc->prpl && gc->prpl->keepalive)
-		gc->prpl->keepalive(gc);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (gc != NULL && gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if (prpl_info && prpl_info->keepalive)
+		prpl_info->keepalive(gc);
+
 	return TRUE;
 }
 
@@ -86,7 +93,9 @@
 
 void serv_close(struct gaim_connection *gc)
 {
-	struct prpl *prpl;
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
 	while (gc->buddy_chats) {
 		struct gaim_conversation *b = gc->buddy_chats->data;
 
@@ -103,14 +112,16 @@
 
 	update_keepalive(gc, FALSE);
 
-	if (gc->prpl && gc->prpl->close)
-		gc->prpl->close(gc);
+	if (gc->prpl != NULL) {
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+		if (prpl_info->close)
+			prpl_info->close(gc);
+	}
 
 	prpl = gc->prpl;
 	account_offline(gc);
 	destroy_gaim_conn(gc);
-
-	unref_protocol(prpl);
 }
 
 void serv_touch_idle(struct gaim_connection *gc)
@@ -127,6 +138,11 @@
 
 void serv_finish_login(struct gaim_connection *gc)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (gc != NULL && gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
 	if (strlen(gc->account->user_info)) {
 		/* g_malloc(strlen(gc->user->user_info) * 4);
 		   strncpy_withhtml(buf, gc->user->user_info, strlen(gc->user->user_info) * 4); */
@@ -140,7 +156,7 @@
 	gc->idle_timer = g_timeout_add(20000, check_idle, gc);
 	serv_touch_idle(gc);
 
-	if (gc->prpl->options & OPT_PROTO_CORRECT_TIME)
+	if (prpl_info->options & OPT_PROTO_CORRECT_TIME)
 		serv_add_buddy(gc, gc->username);
 
 	update_keepalive(gc, TRUE);
@@ -151,9 +167,15 @@
  * if it returns zero, it will not send any more typing notifications 
  * typing is a flag - TRUE for typing, FALSE for stopped typing */
 int serv_send_typing(struct gaim_connection *g, char *name, int typing) {
-	if (g && g->prpl && g->prpl->send_typing)
-		return g->prpl->send_typing(g, name, typing);
-	else return 0;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (g && prpl_info && prpl_info->send_typing)
+		return prpl_info->send_typing(g, name, typing);
+
+	return 0;
 }
 
 struct queued_away_response {
@@ -168,11 +190,15 @@
 {
 	struct gaim_conversation *c;
 	int val = -EINVAL;
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (gc != NULL && gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
 
 	c = gaim_find_conversation(name);
 
-	if (gc->prpl && gc->prpl->send_im)
-		val = gc->prpl->send_im(gc, name, message, len, flags);
+	if (prpl_info && prpl_info->send_im)
+		val = prpl_info->send_im(gc, name, message, len, flags);
 
 	if (!(flags & IM_FLAG_AWAY))
 		serv_touch_idle(gc);
@@ -200,20 +226,35 @@
 
 void serv_get_info(struct gaim_connection *g, char *name)
 {
-	if (g && g->prpl && g->prpl->get_info)
-		g->prpl->get_info(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (g && prpl_info && prpl_info->get_info)
+		prpl_info->get_info(g, name);
 }
 
 void serv_get_away(struct gaim_connection *g, const char *name)
 {
-	if (g && g->prpl && g->prpl->get_away)
-		g->prpl->get_away(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (g && prpl_info && prpl_info->get_away)
+		prpl_info->get_away(g, name);
 }
 
 void serv_get_dir(struct gaim_connection *g, char *name)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->get_dir)
-		g->prpl->get_dir(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->get_dir)
+		prpl_info->get_dir(g, name);
 }
 
 void serv_set_dir(struct gaim_connection *g, const char *first,
@@ -221,8 +262,13 @@
 				  const char *city, const char *state, const char *country,
 				  int web)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->set_dir)
-		g->prpl->set_dir(g, first, middle, last, maiden, city, state,
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->set_dir)
+		prpl_info->set_dir(g, first, middle, last, maiden, city, state,
 						 country, web);
 }
 
@@ -231,15 +277,25 @@
 		     const char *city, const char *state, const char *country,
 			 const char *email)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->dir_search)
-		g->prpl->dir_search(g, first, middle, last, maiden, city, state,
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->dir_search)
+		prpl_info->dir_search(g, first, middle, last, maiden, city, state,
 							country, email);
 }
 
 
 void serv_set_away(struct gaim_connection *gc, char *state, char *message)
 {
-	if (gc && gc->prpl && gc->prpl->set_away) {
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (gc != NULL && gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if (prpl_info && prpl_info->set_away) {
 		char *buf = NULL;
 
 		if (gc->away_state) {
@@ -255,13 +311,13 @@
 				strncpy_nohtml(buf, message, strlen(message) + 1);
 		}
 
-		gc->prpl->set_away(gc, state, buf);
+		prpl_info->set_away(gc, state, buf);
 
 		if (gc->away && state) {
 			gc->away_state = g_strdup(state);
 		}
 
-		plugin_event(event_away, gc, state, buf);
+		gaim_event_broadcast(event_away, gc, state, buf);
 
 		if (buf)
 			g_free(buf);
@@ -272,65 +328,99 @@
 
 void serv_set_away_all(char *message)
 {
-	GSList *c = connections;
+	GSList *c;
 	struct gaim_connection *g;
 
-	while (c) {
+	for (c = connections; c != NULL; c = c->next) {
 		g = (struct gaim_connection *)c->data;
+
 		serv_set_away(g, GAIM_AWAY_CUSTOM, message);
-		c = c->next;
 	}
 }
 
 void serv_set_info(struct gaim_connection *g, char *info)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->set_info) {
-		if (plugin_event(event_set_info, g, info))
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->set_info) {
+		if (gaim_event_broadcast(event_set_info, g, info))
 			return;
-		g->prpl->set_info(g, info);
+
+		prpl_info->set_info(g, info);
 	}
 }
 
 void serv_change_passwd(struct gaim_connection *g, const char *orig, const char *new)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->change_passwd)
-		g->prpl->change_passwd(g, orig, new);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->change_passwd)
+		prpl_info->change_passwd(g, orig, new);
 }
 
 void serv_add_buddy(struct gaim_connection *g, const char *name)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->add_buddy)
-		g->prpl->add_buddy(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->add_buddy)
+		prpl_info->add_buddy(g, name);
 }
 
 void serv_add_buddies(struct gaim_connection *g, GList *buddies)
 {
-	if (g && g_slist_find(connections, g) && g->prpl) {
-		if (g->prpl->add_buddies)
-			g->prpl->add_buddies(g, buddies);
-		else if (g->prpl->add_buddy)
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g)) {
+		if (prpl_info->add_buddies)
+			prpl_info->add_buddies(g, buddies);
+		else if (prpl_info->add_buddy) {
 			while (buddies) {
-				g->prpl->add_buddy(g, buddies->data);
+				prpl_info->add_buddy(g, buddies->data);
 				buddies = buddies->next;
 			}
+		}
 	}
 }
 
 
 void serv_remove_buddy(struct gaim_connection *g, char *name, char *group)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->remove_buddy)
-		g->prpl->remove_buddy(g, name, group);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->remove_buddy)
+		prpl_info->remove_buddy(g, name, group);
 }
 
 void serv_remove_buddies(struct gaim_connection *gc, GList *g, char *group)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
 	if (!g_slist_find(connections, gc))
 		return;
+
 	if (!gc->prpl)
 		return;		/* how the hell did that happen? */
-	if (gc->prpl->remove_buddies)
-		gc->prpl->remove_buddies(gc, g, group);
+
+	if (gc != NULL && gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	if (prpl_info->remove_buddies)
+		prpl_info->remove_buddies(gc, g, group);
 	else {
 		while (g) {
 			serv_remove_buddy(gc, g->data, group);
@@ -344,8 +434,13 @@
  */
 void serv_alias_buddy(struct buddy *b)
 {
-	if(b && b->account->gc && b->account->gc->prpl && b->account->gc->prpl->alias_buddy) {
-		b->account->gc->prpl->alias_buddy(b->account->gc, b->name, b->alias);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (b != NULL && b->account->gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl);
+
+	if (b && prpl_info && prpl_info->alias_buddy) {
+		prpl_info->alias_buddy(b->account->gc, b->name, b->alias);
 	}
 }
 
@@ -373,9 +468,14 @@
  */
 void serv_move_buddy(struct buddy *b, struct group *og, struct group *ng)
 {
-	if(b && b->account->gc && og && ng) {
-		if(b->account->gc->prpl && b->account->gc->prpl->group_buddy) {
-			b->account->gc->prpl->group_buddy(b->account->gc, b->name, og->name, ng->name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (b->account->gc != NULL && b->account->gc->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl);
+
+	if (b && b->account->gc && og && ng) {
+		if (prpl_info && prpl_info->group_buddy) {
+			prpl_info->group_buddy(b->account->gc, b->name, og->name, ng->name);
 		}
 	}
 }
@@ -383,9 +483,15 @@
 /*
  * Rename a group on server roster/list.
  */
-void serv_rename_group(struct gaim_connection *g, struct group *old_group, const char *new_name)
+void serv_rename_group(struct gaim_connection *g, struct group *old_group,
+					   const char *new_name)
 {
-	if (g && g->prpl && old_group && new_name) {
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && old_group && new_name) {
 		GList *tobemoved = NULL;
 		GaimBlistNode *b = ((GaimBlistNode*)old_group)->child;
 
@@ -396,10 +502,10 @@
 			b = b->next;
 		}
 
-		if (g->prpl->rename_group) {
+		if (prpl_info->rename_group) {
 			/* prpl's might need to check if the group already 
 			 * exists or not, and handle that differently */
-			g->prpl->rename_group(g, old_group->name, new_name, tobemoved);
+			prpl_info->rename_group(g, old_group->name, new_name, tobemoved);
 		} else {
 			serv_remove_buddies(g, tobemoved, old_group->name);
 			serv_add_buddies(g, tobemoved);
@@ -411,87 +517,154 @@
 
 void serv_add_permit(struct gaim_connection *g, const char *name)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->add_permit)
-		g->prpl->add_permit(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->add_permit)
+		prpl_info->add_permit(g, name);
 }
 
 void serv_add_deny(struct gaim_connection *g, const char *name)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->add_deny)
-		g->prpl->add_deny(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->add_deny)
+		prpl_info->add_deny(g, name);
 }
 
 void serv_rem_permit(struct gaim_connection *g, const char *name)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->rem_permit)
-		g->prpl->rem_permit(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->rem_permit)
+		prpl_info->rem_permit(g, name);
 }
 
 void serv_rem_deny(struct gaim_connection *g, const char *name)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->rem_deny)
-		g->prpl->rem_deny(g, name);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->rem_deny)
+		prpl_info->rem_deny(g, name);
 }
 
 void serv_set_permit_deny(struct gaim_connection *g)
 {
-	/* this is called when either you import a buddy list, and make lots of changes that way,
-	 * or when the user toggles the permit/deny mode in the prefs. In either case you should
-	 * probably be resetting and resending the permit/deny info when you get this. */
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->set_permit_deny)
-		g->prpl->set_permit_deny(g);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	/*
+	 * this is called when either you import a buddy list, and make lots
+	 * of changes that way, or when the user toggles the permit/deny mode
+	 * in the prefs. In either case you should probably be resetting and
+	 * resending the permit/deny info when you get this.
+	 */
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->set_permit_deny)
+		prpl_info->set_permit_deny(g);
 }
 
 
 void serv_set_idle(struct gaim_connection *g, int time)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->set_idle)
-		g->prpl->set_idle(g, time);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->set_idle)
+		prpl_info->set_idle(g, time);
 }
 
 void serv_warn(struct gaim_connection *g, char *name, int anon)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->warn)
-		g->prpl->warn(g, name, anon);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->warn)
+		prpl_info->warn(g, name, anon);
 }
 
 void serv_join_chat(struct gaim_connection *g, GList *data)
 {
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->join_chat)
-		g->prpl->join_chat(g, data);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->join_chat)
+		prpl_info->join_chat(g, data);
 }
 
 void serv_chat_invite(struct gaim_connection *g, int id, const char *message, const char *name)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	char *buffy = message && *message ? g_strdup(message) : NULL;
-	plugin_event(event_chat_send_invite, g, (void *)id, name, &buffy);
-	if (g && g_slist_find(connections, g) && g->prpl && g->prpl->chat_invite)
-		g->prpl->chat_invite(g, id, buffy, name);
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	gaim_event_broadcast(event_chat_send_invite, g, (void *)id, name, &buffy);
+
+	if (prpl_info && g_slist_find(connections, g) && prpl_info->chat_invite)
+		prpl_info->chat_invite(g, id, buffy, name);
+
 	if (buffy)
 		g_free(buffy);
 }
 
 void serv_chat_leave(struct gaim_connection *g, int id)
 {
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
 	if (!g_slist_find(connections, g))
 		return;
 
-	if (g->prpl && g->prpl->chat_leave)
-		g->prpl->chat_leave(g, id);
+	if (g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && prpl_info->chat_leave)
+		prpl_info->chat_leave(g, id);
 }
 
 void serv_chat_whisper(struct gaim_connection *g, int id, char *who, char *message)
 {
-	if (g->prpl && g->prpl->chat_whisper)
-		g->prpl->chat_whisper(g, id, who, message);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g != NULL && g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && prpl_info->chat_whisper)
+		prpl_info->chat_whisper(g, id, who, message);
 }
 
 int serv_chat_send(struct gaim_connection *g, int id, char *message)
 {
 	int val = -EINVAL;
-	if (g->prpl && g->prpl->chat_send)
-		val = g->prpl->chat_send(g, id, message);
+	GaimPluginProtocolInfo *prpl_info = NULL;
+
+	if (g->prpl != NULL)
+		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
+
+	if (prpl_info && prpl_info->chat_send)
+		val = prpl_info->chat_send(g, id, message);
+
 	serv_touch_idle(g);
+
 	return val;
 }
 
@@ -600,7 +773,7 @@
 		buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG));
 		strcpy(buffy, msg);
 		angel = g_strdup(who);
-		plugin_return = plugin_event(event_im_recv, gc, &angel, &buffy, &flags);
+		plugin_return = gaim_event_broadcast(event_im_recv, gc, &angel, &buffy, &flags);
 
 		if (!buffy || !angel || plugin_return) {
 			if (buffy)
@@ -831,7 +1004,7 @@
 		}
 	}
 
-	plugin_event(event_im_displayed_rcvd, gc, name, message, flags, mtime);
+	gaim_event_broadcast(event_im_displayed_rcvd, gc, name, message, flags, mtime);
 	g_free(name);
 	g_free(message);
 }
@@ -843,7 +1016,9 @@
 {
 	struct buddy *b = gaim_find_buddy(gc->account, name);
 
-	if (signon && (gc->prpl->options & OPT_PROTO_CORRECT_TIME)) {
+	if (signon && (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->options &
+				   OPT_PROTO_CORRECT_TIME)) {
+
 		char *tmp = g_strdup(normalize(name));
 		if (!gaim_utf8_strcasecmp(tmp, normalize(gc->username))) {
 			gc->evil = evil;
@@ -868,12 +1043,12 @@
 
 	if (!b->idle && idle) {
 		gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_IDLE);
-		plugin_event(event_buddy_idle, gc, b->name);
+		gaim_event_broadcast(event_buddy_idle, gc, b->name);
 		system_log(log_idle, gc, b, OPT_LOG_BUDDY_IDLE);
 	}
 	if (b->idle && !idle) {
 		gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_IDLE_RETURN);
-		plugin_event(event_buddy_unidle, gc, b->name);
+		gaim_event_broadcast(event_buddy_unidle, gc, b->name);
 		system_log(log_unidle, gc, b, OPT_LOG_BUDDY_IDLE);
 	}
 
@@ -948,7 +1123,7 @@
 {
 	char buf2[1024];
 
-	plugin_event(event_warned, gc, name, lev);
+	gaim_event_broadcast(event_warned, gc, name, lev);
 
 	if (gc->evil >= lev) {
 		gc->evil = lev;
@@ -984,7 +1159,7 @@
 
 	b = gaim_find_buddy(gc->account, name);
 
-	plugin_event(event_got_typing, gc, name);
+	gaim_event_broadcast(event_got_typing, gc, name);
 
 	if (b != NULL)
 		gaim_pounce_execute(gc->account, name, GAIM_POUNCE_TYPING);
@@ -1048,7 +1223,7 @@
 	struct chat_invite_data *cid = g_new0(struct chat_invite_data, 1);
 
 
-	plugin_event(event_chat_invited, gc, who, name, message);
+	gaim_event_broadcast(event_chat_invited, gc, who, name, message);
 
 	if (message)
 		g_snprintf(buf2, sizeof(buf2),
@@ -1106,7 +1281,7 @@
 	gaim_window_switch_conversation(gaim_conversation_get_window(b),
 									gaim_conversation_get_index(b));
 
-	plugin_event(event_chat_join, gc, id, name);
+	gaim_event_broadcast(event_chat_join, gc, id, name);
 
 	return b;
 }
@@ -1131,7 +1306,7 @@
 	if (!conv)
 		return;
 
-	plugin_event(event_chat_leave, g, gaim_chat_get_id(chat));
+	gaim_event_broadcast(event_chat_leave, g, gaim_chat_get_id(chat));
 
 	debug_printf("Leaving room %s.\n", gaim_conversation_get_name(conv));
 
@@ -1176,7 +1351,7 @@
 	buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG));
 	strcpy(buffy, message);
 	angel = g_strdup(who);
-	plugin_return = plugin_event(event_chat_recv, g, gaim_chat_get_id(chat),
+	plugin_return = gaim_event_broadcast(event_chat_recv, g, gaim_chat_get_id(chat),
 								 &angel, &buffy);
 
 	if (!buffy || !angel || plugin_return) {
--- a/src/themes.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/themes.c	Fri Apr 25 06:47:33 2003 +0000
@@ -237,7 +237,7 @@
 }
 
 GSList *get_proto_smileys(int protocol) {
-	struct prpl *proto = find_prpl(protocol);
+	GaimPlugin *proto = gaim_find_prpl(protocol);
 	struct smiley_list *list, *def;
 
 	if(!current_smiley_theme)
@@ -248,7 +248,7 @@
 	while(list) {
 		if(!strcmp(list->sml, "default"))
 			def = list;
-		else if(proto && !strcmp(proto->name, list->sml))
+		else if(proto && !strcmp(proto->info->name, list->sml))
 			break;
 
 		list = list->next;
--- a/src/util.c	Thu Apr 24 03:52:25 2003 +0000
+++ b/src/util.c	Fri Apr 25 06:47:33 2003 +0000
@@ -997,7 +997,7 @@
 	   AIM account connected. */
 	while (conn) {
 		gc = conn->data;
-		if (gc->protocol == PROTO_OSCAR && isalpha(gc->username[0])) {
+		if (gc->protocol == GAIM_PROTO_OSCAR && isalpha(gc->username[0])) {
 			break;
 		}
 		conn = conn->next;