changeset 17568:980a104267da

Patch from Pekka Riikonen to update the SILC protocol plugin to work with SILC Toolkit 1.1 I added the fallback to SILC Toolkit 1.0 support (silc10 protocol directory) and configure.ac adjustments, any problems with this are 100% my fault.
author Stu Tomlinson <stu@nosnilmot.com>
date Sat, 09 Jun 2007 17:31:28 +0000
parents ba1b50f114f6
children b5fdfb305f20
files ChangeLog configure.ac libpurple/protocols/Makefile.am libpurple/protocols/silc/README libpurple/protocols/silc/TODO libpurple/protocols/silc/buddy.c libpurple/protocols/silc/chat.c libpurple/protocols/silc/ft.c libpurple/protocols/silc/ops.c libpurple/protocols/silc/pk.c libpurple/protocols/silc/silc.c libpurple/protocols/silc/silcpurple.h libpurple/protocols/silc/util.c libpurple/protocols/silc/wb.c
diffstat 14 files changed, 1666 insertions(+), 1618 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jun 09 16:39:00 2007 +0000
+++ b/ChangeLog	Sat Jun 09 17:31:28 2007 +0000
@@ -13,6 +13,7 @@
 	* Remove MSN's random "Authorization Failed" dialogs
 	* Fix MSN to correctly detect incorrect passwords and disable the account
 	* Get User Info on MSN is now more reliable & accurate
+	* Updated SILC protocol to support SILC Toolkit 1.1 (Pekka Riikonen)
 
 	Finch:
 	* Auto account reconnecting
--- a/configure.ac	Sat Jun 09 16:39:00 2007 +0000
+++ b/configure.ac	Sat Jun 09 17:31:28 2007 +0000
@@ -647,13 +647,14 @@
 AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"])
 SILC_CFLAGS=""
 SILC_LIBS=""
+have_silc="no"
 if test -n "$with_silc_includes" || test -n "$with_silc_libs"; then
 	silc_manual_check="yes"
 else
 	silc_manual_check="no"
 fi
 if test "x$silc_manual_check" = "xno"; then
-	PKG_CHECK_MODULES(SILC, silcclient, [
+	PKG_CHECK_MODULES(SILC, [silcclient >= 1.1], [
 		have_silc="yes"
 		silcincludes="yes"
 		silcclient="yes"
@@ -661,16 +662,26 @@
 		AC_MSG_RESULT(no)
 		have_silc="no"
 	])
-	dnl If silcclient.pc wasn't found, check for just silc.pc
 	if test "x$have_silc" = "xno"; then
-		PKG_CHECK_MODULES(SILC, silc, [
+		PKG_CHECK_MODULES(SILC, silcclient, [
 			have_silc="yes"
-			silcincludes="yes"
-			silcclient="yes"
+			silc10includes="yes"
+			silc10client="yes"
 		], [
 			AC_MSG_RESULT(no)
 			have_silc="no"
 		])
+		dnl If silcclient.pc wasn't found, check for just silc.pc
+		if test "x$have_silc" = "xno"; then
+			PKG_CHECK_MODULES(SILC, silc, [
+				have_silc="yes"
+				silc10includes="yes"
+				silc10client="yes"
+			], [
+				AC_MSG_RESULT(no)
+				have_silc="no"
+			])
+		fi
 	fi
 else
 	if test "$ac_silc_includes" != "no"; then
@@ -678,7 +689,7 @@
 	fi
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
-	AC_CHECK_HEADER(silcincludes.h, [silcincludes=yes])
+	AC_CHECK_HEADER(silc.h, [silcincludes=yes])
 	CPPFLAGS="$CPPFLAGS_save"
 
 	if test "$ac_silc_libs" != "no"; then
@@ -686,11 +697,28 @@
 	fi
 	SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL"
 	AC_CHECK_LIB(silcclient, silc_client_init, [silcclient=yes], , $SILC_LIBS)
+
+	if test "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes"; then
+		have_silc="yes"
+	else
+		CPPFLAGS_save="$CPPFLAGS"
+		CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
+		AC_CHECK_HEADER(silcincludes.h, [silc10includes=yes])
+		CPPFLAGS="$CPPFLAGS_save"
+
+		SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL"
+		AC_CHECK_LIB(silcclient, silc_client_init, [silc10client=yes], , $SILC_LIBS)
+		if test "x$silc10includes" = "xyes" -a "x$silc10client" = "xyes"; then
+			have_silc="yes"
+		fi
+	fi
 fi
 AC_SUBST(SILC_LIBS)
 AC_SUBST(SILC_CFLAGS)
 dnl SILC Toolkit >= 1.0.1 has a new MIME API
 if test "x$silcclient" = "xyes"; then
+	AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
+elif test "x$silc10client" = "xyes"; then
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
 		AC_MSG_CHECKING(for silcmime.h)
@@ -795,7 +823,10 @@
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
 fi
 if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
-	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc//'`
+	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'`
+fi
+if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
+	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc10//'`
 fi
 AC_SUBST(STATIC_PRPLS)
 STATIC_LINK_LIBS=
@@ -813,6 +844,8 @@
 	else
 		if test "x$i" = "xsilc"; then
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.a"
+		elif test "x$i" = "xsilc10"; then
+			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.a"
 		else
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.a"
 		fi
@@ -832,6 +865,7 @@
 		qq)			static_qq=yes ;;
 		sametime)	static_sametime=yes ;;
 		silc)		static_silc=yes ;;
+		silc10)		static_silc=yes ;;
 		simple)		static_simple=yes ;;
 		toc)		static_toc=yes ;;
 		yahoo)		static_yahoo=yes ;;
@@ -848,7 +882,7 @@
 AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes")
 AM_CONDITIONAL(STATIC_QQ, test "x$static_qq" = "xyes")
 AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
-AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes")
+AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$have_silc" = "xyes")
 AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes")
 AM_CONDITIONAL(STATIC_TOC, test "x$static_toc" = "xyes")
 AM_CONDITIONAL(STATIC_YAHOO, test "x$static_yahoo" = "xyes")
@@ -868,7 +902,10 @@
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
 fi
 if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
-	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc//'`
+	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'`
+fi
+if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
+	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc10//'`
 fi
 AC_SUBST(DYNAMIC_PRPLS)
 for i in $DYNAMIC_PRPLS ; do
@@ -885,6 +922,7 @@
 		qq)			dynamic_qq=yes ;;
 		sametime)	dynamic_sametime=yes ;;
 		silc)		dynamic_silc=yes ;;
+		silc10)		dynamic_silc=yes ;;
 		simple)		dynamic_simple=yes ;;
 		toc)		dynamic_toc=yes ;;
 		yahoo)		dynamic_yahoo=yes ;;
@@ -901,7 +939,7 @@
 AM_CONDITIONAL(DYNAMIC_OSCAR, test "x$dynamic_oscar" = "xyes")
 AM_CONDITIONAL(DYNAMIC_QQ, test "x$dynamic_qq" = "xyes")
 AM_CONDITIONAL(DYNAMIC_SAMETIME, test "x$dynamic_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
-AM_CONDITIONAL(DYNAMIC_SILC, test "x$dynamic_silc" = "xyes" -a "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes")
+AM_CONDITIONAL(DYNAMIC_SILC, test "x$dynamic_silc" = "xyes" -a "x$have_silc" = "xyes")
 AM_CONDITIONAL(DYNAMIC_SIMPLE, test "x$dynamic_simple" = "xyes")
 AM_CONDITIONAL(DYNAMIC_TOC, test "x$dynamic_toc" = "xyes")
 AM_CONDITIONAL(DYNAMIC_YAHOO, test "x$dynamic_yahoo" = "xyes")
@@ -2100,6 +2138,7 @@
 		   libpurple/protocols/qq/Makefile
 		   libpurple/protocols/sametime/Makefile
 		   libpurple/protocols/silc/Makefile
+		   libpurple/protocols/silc10/Makefile
 		   libpurple/protocols/simple/Makefile
 		   libpurple/protocols/toc/Makefile
 		   libpurple/protocols/yahoo/Makefile
--- a/libpurple/protocols/Makefile.am	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/Makefile.am	Sat Jun 09 17:31:28 2007 +0000
@@ -1,5 +1,5 @@
 EXTRA_DIST = Makefile.mingw
 
-DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc toc simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc silc10 toc simple yahoo zephyr
 
 SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
--- a/libpurple/protocols/silc/README	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/README	Sat Jun 09 17:31:28 2007 +0000
@@ -2,19 +2,19 @@
 ==================
 
 This is the Purple protocol plugin of the protocol called Secure Internet
-Live Conferencing (SILC).  The implementation will use the SILC Toolkit, 
-freely available from the http://silcnet.org/ site, for the actual SILC 
+Live Conferencing (SILC).  The implementation will use the SILC Toolkit,
+freely available from the http://silcnet.org/ site, for the actual SILC
 protocol implementation.
 
-To include SILC into Purple, one needs to first compile and install 
+To include SILC into Purple, one needs to first compile and install
 the SILC Toolkit.  It is done as follows:
 
-	./configure --enable-shared
+	./configure
 	make
 	make install
 
-This will compile shared libraries of the SILC Toolkit.  If the --prefix 
-is not given to ./configure, the binaries are installed into the 
+This will compile shared libraries of the SILC Toolkit.  If the --prefix
+is not given to ./configure, the binaries are installed into the
 /usr/local/silc directory.
 
 Once the Toolkit is installed one needs to tell Purple's ./configure
--- a/libpurple/protocols/silc/TODO	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/TODO	Sat Jun 09 17:31:28 2007 +0000
@@ -1,14 +1,6 @@
 Features TODO (maybe)
 =====================
 
-Sending images
-	- Sending images to channel too, if libpurple allows it.
-
 Preferences
 	- Add joined channels to buddy list automatically (during
 	  session)
-	- Add joined channels to buddy list automatically permanently
-
-Buddy icon
-	- After SILC Toolkit 1.0.2 buddy icon support can be added
-	  (SILC_ATTERIBUTE_USER_ICON).
--- a/libpurple/protocols/silc/buddy.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/buddy.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 #include "wb.h"
@@ -29,7 +29,7 @@
 
 static void
 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
-			 			 gboolean force_local);
+			   gboolean force_local);
 
 typedef struct {
 	char *nick;
@@ -38,10 +38,10 @@
 
 static void
 silcpurple_buddy_keyagr_resolved(SilcClient client,
-			       SilcClientConnection conn,
-			       SilcClientEntry *clients,
-			       SilcUInt32 clients_count,
-			       void *context)
+				 SilcClientConnection conn,
+				 SilcStatus status,
+				 SilcDList clients,
+				 void *context)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurpleResolve r = context;
@@ -62,21 +62,16 @@
 	silc_free(r);
 }
 
-typedef struct {
-	gboolean responder;
-} *SilcPurpleKeyAgr;
-
 static void
 silcpurple_buddy_keyagr_cb(SilcClient client,
-			 SilcClientConnection conn,
-			 SilcClientEntry client_entry,
-			 SilcKeyAgreementStatus status,
-			 SilcSKEKeyMaterial *key,
-			 void *context)
+			   SilcClientConnection conn,
+			   SilcClientEntry client_entry,
+			   SilcKeyAgreementStatus status,
+			   SilcSKEKeyMaterial key,
+			   void *context)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
-	SilcPurpleKeyAgr a = context;
 
 	if (!sg->conn)
 		return;
@@ -90,13 +85,13 @@
 			/* Set the private key for this client */
 			silc_client_del_private_message_key(client, conn, client_entry);
 			silc_client_add_private_message_key_ske(client, conn, client_entry,
-								NULL, NULL, key, a->responder);
+								NULL, NULL, key);
 			silc_ske_free_key_material(key);
 
-			
+
 			/* Open IM window */
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-									client_entry->nickname, sg->account);
+								      client_entry->nickname, sg->account);
 			if (convo) {
 				/* we don't have windows in the core anymore...but we may want to
 				 * provide some method for asking the UI to show the window
@@ -104,7 +99,7 @@
 				 */
 			} else {
 				convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account,
-							      client_entry->nickname);
+								client_entry->nickname);
 			}
 			g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
 			purple_conversation_set_title(convo, tmp);
@@ -113,7 +108,7 @@
 
 	case SILC_KEY_AGREEMENT_ERROR:
 		purple_notify_error(gc, _("Key Agreement"),
-				  _("Error occurred during key agreement"), NULL);
+				    _("Error occurred during key agreement"), NULL);
 		break;
 
 	case SILC_KEY_AGREEMENT_FAILURE:
@@ -122,53 +117,48 @@
 
 	case SILC_KEY_AGREEMENT_TIMEOUT:
 		purple_notify_error(gc, _("Key Agreement"),
-				  _("Timeout during key agreement"), NULL);
+				    _("Timeout during key agreement"), NULL);
 		break;
 
 	case SILC_KEY_AGREEMENT_ABORTED:
 		purple_notify_error(gc, _("Key Agreement"),
-				  _("Key agreement was aborted"), NULL);
+				    _("Key agreement was aborted"), NULL);
 		break;
 
 	case SILC_KEY_AGREEMENT_ALREADY_STARTED:
 		purple_notify_error(gc, _("Key Agreement"),
-				  _("Key agreement is already started"), NULL);
+				    _("Key agreement is already started"), NULL);
 		break;
 
 	case SILC_KEY_AGREEMENT_SELF_DENIED:
 		purple_notify_error(gc, _("Key Agreement"),
-				  _("Key agreement cannot be started with yourself"),
-				  NULL);
+				    _("Key agreement cannot be started with yourself"),
+				    NULL);
 		break;
 
 	default:
 		break;
 	}
-
-	silc_free(a);
 }
 
 static void
 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
-			 gboolean force_local)
+			   gboolean force_local)
 {
 	SilcPurple sg = gc->proto_data;
-	SilcClientEntry *clients;
-	SilcUInt32 clients_count;
+	SilcDList clients;
+	SilcClientEntry client_entry;
+	SilcClientConnectionParams params;
 	char *local_ip = NULL, *remote_ip = NULL;
 	gboolean local = TRUE;
-	char *nickname;
-	SilcPurpleKeyAgr a;
+	SilcSocket sock;
 
 	if (!sg->conn || !name)
 		return;
 
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
-		return;
-
 	/* Find client entry */
-	clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name,
-						&clients_count);
+	clients = silc_client_get_clients_local(sg->client, sg->conn, name,
+						FALSE);
 	if (!clients) {
 		/* Resolve unknown user */
 		SilcPurpleResolve r = silc_calloc(1, sizeof(*r));
@@ -176,12 +166,14 @@
 			return;
 		r->nick = g_strdup(name);
 		r->gc = gc;
-		silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
+		silc_client_get_clients(sg->client, sg->conn, name, NULL,
 					silcpurple_buddy_keyagr_resolved, r);
-		silc_free(nickname);
 		return;
 	}
 
+	silc_socket_stream_get_info(silc_packet_stream_get_stream(sg->conn->stream),
+				    &sock, NULL, NULL, NULL);
+
 	/* Resolve the local IP from the outgoing socket connection.  We resolve
 	   it to check whether we have a private range IP address or public IP
 	   address.  If we have public then we will assume that we are not behind
@@ -196,14 +188,14 @@
 
 	   Naturally this algorithm does not always get things right. */
 
-	if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) {
+	if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
 		/* Check if the IP is private */
 		if (!force_local && silcpurple_ip_is_private(local_ip)) {
 			local = FALSE;
 
 			/* Local IP is private, resolve the remote server IP to see whether
 			   we are talking to Internet or just on LAN. */
-			if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL,
+			if (silc_net_check_host_by_sock(sock, NULL,
 							&remote_ip))
 				if (silcpurple_ip_is_private(remote_ip))
 					/* We assume we are in LAN.  Let's provide
@@ -218,19 +210,24 @@
 	if (local && !local_ip)
 		local_ip = silc_net_localip();
 
-	a = silc_calloc(1, sizeof(*a));
-	if (!a)
-		return;
-	a->responder = local;
+	silc_dlist_start(clients);
+	client_entry = silc_dlist_get(clients);
+
+	memset(&params, 0, sizeof(params));
+	params.timeout_secs = 60;
+	if (local)
+	  /* Provide connection point */
+	  params.local_ip = local_ip;
 
 	/* Send the key agreement request */
-	silc_client_send_key_agreement(sg->client, sg->conn, clients[0],
-				       local ? local_ip : NULL, NULL, 0, 60,
-				       silcpurple_buddy_keyagr_cb, a);
+	silc_client_send_key_agreement(sg->client, sg->conn, client_entry,
+				       &params, sg->public_key,
+				       sg->private_key,
+				       silcpurple_buddy_keyagr_cb, NULL);
 
 	silc_free(local_ip);
 	silc_free(remote_ip);
-	silc_free(clients);
+	silc_client_list_free(sg->client, sg->conn, clients);
 }
 
 typedef struct {
@@ -244,8 +241,8 @@
 static void
 silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
 {
-	SilcPurpleKeyAgr ai;
 	SilcClientEntry client_entry;
+	SilcClientConnectionParams params;
 
 	if (id != 1)
 		goto out;
@@ -255,26 +252,27 @@
 						    &a->client_id);
 	if (!client_entry) {
 		purple_notify_error(a->client->application, _("Key Agreement"),
-				  _("The remote user is not present in the network any more"),
-				  NULL);
+				    _("The remote user is not present in the network any more"),
+				    NULL);
 		goto out;
 	}
 
 	/* If the hostname was provided by the requestor perform the key agreement
 	   now.  Otherwise, we will send him a request to connect to us. */
 	if (a->hostname) {
-		ai = silc_calloc(1, sizeof(*ai));
-		if (!ai)
-			goto out;
-		ai->responder = FALSE;
-		silc_client_perform_key_agreement(a->client, a->conn, client_entry,
+		memset(&params, 0, sizeof(params));
+		params.timeout_secs = 60;
+		silc_client_perform_key_agreement(a->client, a->conn,
+						  client_entry, &params,
+						  a->conn->public_key,
+						  a->conn->private_key,
 						  a->hostname, a->port,
-						  silcpurple_buddy_keyagr_cb, ai);
+						  silcpurple_buddy_keyagr_cb, NULL);
 	} else {
 		/* Send request.  Force us as the point of connection since requestor
 		   did not provide the point of connection. */
 		silcpurple_buddy_keyagr_do(a->client->application,
-					 client_entry->nickname, TRUE);
+					   client_entry->nickname, TRUE);
 	}
 
  out:
@@ -283,14 +281,19 @@
 }
 
 void silcpurple_buddy_keyagr_request(SilcClient client,
-				   SilcClientConnection conn,
-				   SilcClientEntry client_entry,
-				   const char *hostname, SilcUInt16 port)
+				     SilcClientConnection conn,
+				     SilcClientEntry client_entry,
+				     const char *hostname, SilcUInt16 port,
+				     SilcUInt16 protocol)
 {
 	char tmp[128], tmp2[128];
 	SilcPurpleKeyAgrAsk a;
 	PurpleConnection *gc = client->application;
 
+	/* For now Pidgin don't support UDP key agreement */
+	if (protocol == 1)
+	  return;
+
 	g_snprintf(tmp, sizeof(tmp),
 		   _("Key agreement request received from %s. Would you like to "
 		     "perform the key agreement?"), client_entry->nickname);
@@ -304,15 +307,15 @@
 		return;
 	a->client = client;
 	a->conn = conn;
-	a->client_id = *client_entry->id;
+	a->client_id = client_entry->id;
 	if (hostname)
 		a->hostname = strdup(hostname);
 	a->port = port;
 
 	purple_request_action(client->application, _("Key Agreement Request"), tmp,
-			    hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
-				NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
-			    _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
+			      hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
+			      NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
+			      _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
 }
 
 static void
@@ -333,9 +336,7 @@
 	PurpleBuddy *b;
 	PurpleConnection *gc;
         SilcPurple sg;
-	char *nickname;
-	SilcClientEntry *clients;
-	SilcUInt32 clients_count;
+	SilcDList clients;
 
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
@@ -343,23 +344,16 @@
 	gc = purple_account_get_connection(b->account);
 	sg = gc->proto_data;
 
-	if (!silc_parse_userfqdn(b->name, &nickname, NULL))
-		return;
-
 	/* Find client entry */
 	clients = silc_client_get_clients_local(sg->client, sg->conn,
-						nickname, b->name,
-						&clients_count);
-	if (!clients) {
-		silc_free(nickname);
+						b->name, FALSE);
+	if (!clients)
 		return;
-	}
 
-	clients[0]->prv_resp = FALSE;
+	silc_dlist_start(clients);
 	silc_client_del_private_message_key(sg->client, sg->conn,
-					    clients[0]);
-	silc_free(clients);
-	silc_free(nickname);
+					    silc_dlist_get(clients));
+	silc_client_list_free(sg->client, sg->conn, clients);
 }
 
 typedef struct {
@@ -386,8 +380,8 @@
 						    &p->client_id);
 	if (!client_entry) {
 		purple_notify_error(p->client->application, _("IM With Password"),
-				  _("The remote user is not present in the network any more"),
-				  NULL);
+				    _("The remote user is not present in the network any more"),
+				    NULL);
 		silc_free(p);
 		return;
 	}
@@ -398,21 +392,16 @@
 	silc_client_add_private_message_key(p->client, p->conn,
 					    client_entry, NULL, NULL,
 					    (unsigned char *)passphrase,
-					    strlen(passphrase), FALSE,
-					    client_entry->prv_resp);
-	if (!client_entry->prv_resp)
-		silc_client_send_private_message_key_request(p->client,
-							     p->conn,
-							     client_entry);
+					    strlen(passphrase));
         silc_free(p);
 }
 
 static void
 silcpurple_buddy_privkey_resolved(SilcClient client,
-				SilcClientConnection conn,
-				SilcClientEntry *clients,
-				SilcUInt32 clients_count,
-				void *context)
+				  SilcClientConnection conn,
+				  SilcStatus status,
+				  SilcDList clients,
+				  void *context)
 {
 	char tmp[256];
 
@@ -434,42 +423,39 @@
 silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
 {
 	SilcPurple sg = gc->proto_data;
-	char *nickname;
 	SilcPurplePrivkey p;
-	SilcClientEntry *clients;
-	SilcUInt32 clients_count;
+	SilcDList clients;
+	SilcClientEntry client_entry;
 
 	if (!name)
 		return;
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
-		return;
 
 	/* Find client entry */
 	clients = silc_client_get_clients_local(sg->client, sg->conn,
-						nickname, name,
-						&clients_count);
+						name, FALSE);
 	if (!clients) {
-		silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
+		silc_client_get_clients(sg->client, sg->conn, name, NULL,
 					silcpurple_buddy_privkey_resolved,
 					g_strdup(name));
-		silc_free(nickname);
 		return;
 	}
 
+	silc_dlist_start(clients);
+	client_entry = silc_dlist_get(clients);
+
 	p = silc_calloc(1, sizeof(*p));
 	if (!p)
 		return;
 	p->client = sg->client;
 	p->conn = sg->conn;
-	p->client_id = *clients[0]->id;
+	p->client_id = client_entry->id;
 	purple_request_input(gc, _("IM With Password"), NULL,
 	                     _("Set IM Password"), NULL, FALSE, TRUE, NULL,
 	                     _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
 	                     _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
 	                     gc->account, NULL, NULL, p);
 
-	silc_free(clients);
-	silc_free(nickname);
+	silc_client_list_free(sg->client, sg->conn, clients);
 }
 
 static void
@@ -498,13 +484,21 @@
 static void
 silcpurple_buddy_getkey(PurpleConnection *gc, const char *name);
 
-static void
-silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g,
-			 SilcClientCommandReplyContext cmd)
+static SilcBool
+silcpurple_buddy_getkey_cb(SilcClient client, SilcClientConnection conn,
+			   SilcCommand command, SilcStatus status,
+			   SilcStatus error, void *context, va_list ap)
 {
 	SilcClientEntry client_entry;
-	unsigned char *pk;
-	SilcUInt32 pk_len;
+	SilcPurpleBuddyGetkey g = context;
+
+	if (status != SILC_STATUS_OK) {
+		purple_notify_error(g->client->application, _("Get Public Key"),
+				  _("The remote user is not present in the network any more"),
+				  NULL);
+		silc_free(g);
+		return FALSE;
+	}
 
 	/* Get the client entry. */
 	client_entry = silc_client_get_client_by_id(g->client, g->conn,
@@ -514,30 +508,28 @@
 				  _("The remote user is not present in the network any more"),
 				  NULL);
 		silc_free(g);
-		return;
+		return FALSE;
 	}
 
 	if (!client_entry->public_key) {
 		silc_free(g);
-		return;
+		return FALSE;
 	}
 
 	/* Now verify the public key */
-	pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
 	silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname,
-				   SILC_SOCKET_TYPE_CLIENT,
-				   pk, pk_len, SILC_SKE_PK_TYPE_SILC,
-				   NULL, NULL);
-	silc_free(pk);
+				     SILC_CONN_CLIENT, client_entry->public_key,
+				     NULL, NULL);
 	silc_free(g);
+	return TRUE;
 }
 
 static void
 silcpurple_buddy_getkey_resolved(SilcClient client,
-			       SilcClientConnection conn,
-			       SilcClientEntry *clients,
-			       SilcUInt32 clients_count,
-			       void *context)
+				 SilcClientConnection conn,
+				 SilcStatus status,
+				 SilcDList clients,
+				 void *context)
 {
 	char tmp[256];
 
@@ -546,7 +538,7 @@
 			   _("User %s is not present in the network"),
 			   (const char *)context);
 		purple_notify_error(client->application, _("Get Public Key"),
-				  _("Cannot fetch the public key"), tmp);
+				    _("Cannot fetch the public key"), tmp);
 		g_free(context);
 		return;
 	}
@@ -561,42 +553,38 @@
 	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
-	SilcClientEntry *clients;
-	SilcUInt32 clients_count;
+	SilcClientEntry client_entry;
+	SilcDList clients;
 	SilcPurpleBuddyGetkey g;
-	char *nickname;
+	SilcUInt16 cmd_ident;
 
 	if (!name)
 		return;
 
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
-		return;
-
 	/* Find client entry */
-	clients = silc_client_get_clients_local(client, conn, nickname, name,
-						&clients_count);
+	clients = silc_client_get_clients_local(client, conn, name, FALSE);
 	if (!clients) {
-		silc_client_get_clients(client, conn, nickname, NULL,
+		silc_client_get_clients(client, conn, name, NULL,
 					silcpurple_buddy_getkey_resolved,
 					g_strdup(name));
-		silc_free(nickname);
 		return;
 	}
 
+	silc_dlist_start(clients);
+	client_entry = silc_dlist_get(clients);
+
 	/* Call GETKEY */
 	g = silc_calloc(1, sizeof(*g));
 	if (!g)
 		return;
 	g->client = client;
 	g->conn = conn;
-	g->client_id = *clients[0]->id;
-	silc_client_command_call(client, conn, NULL, "GETKEY",
-				 clients[0]->nickname, NULL);
-	silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
-				    conn->cmd_ident,
-				    (SilcCommandCb)silcpurple_buddy_getkey_cb, g);
-	silc_free(clients);
-	silc_free(nickname);
+	g->client_id = client_entry->id;
+	cmd_ident = silc_client_command_call(client, conn, NULL, "GETKEY",
+					     client_entry->nickname, NULL);
+	silc_client_command_pending(conn, SILC_COMMAND_GETKEY, cmd_ident,
+				    silcpurple_buddy_getkey_cb, g);
+	silc_client_list_free(client, conn, clients);
 }
 
 static void
@@ -629,8 +617,7 @@
 	sg = gc->proto_data;
 
 	pkfile = purple_blist_node_get_string(node, "public-key");
-	if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
-	    !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
+	if (!silc_pkcs_load_public_key(pkfile, &public_key)) {
 		purple_notify_error(gc,
 				  _("Show Public Key"),
 				  _("Could not load public key"), NULL);
@@ -661,6 +648,7 @@
 	PurpleBuddy *b;
 	unsigned char *offline_pk;
 	SilcUInt32 offline_pk_len;
+	SilcPublicKey public_key;
 	unsigned int offline        : 1;
 	unsigned int pubkey_search  : 1;
 	unsigned int init           : 1;
@@ -670,10 +658,10 @@
 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id);
 static void
 silcpurple_add_buddy_resolved(SilcClient client,
-			    SilcClientConnection conn,
-			    SilcClientEntry *clients,
-			    SilcUInt32 clients_count,
-			    void *context);
+			      SilcClientConnection conn,
+			      SilcStatus status,
+			      SilcDList clients,
+			      void *context);
 
 void silcpurple_get_info(PurpleConnection *gc, const char *who)
 {
@@ -735,35 +723,38 @@
 	g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
 		   r->b->name);
 	purple_notify_error(r->client->application, _("Add Buddy"), tmp,
-			  _("You cannot receive buddy notifications until you "
-			    "import his/her public key.  You can use the Get Public Key "
-			    "command to get the public key."));
+			    _("You cannot receive buddy notifications until you "
+			      "import his/her public key.  You can use the Get Public Key "
+			      "command to get the public key."));
 	purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
 }
 
 static void
-silcpurple_add_buddy_save(bool success, void *context)
+silcpurple_add_buddy_save(SilcBool success, void *context)
 {
 	SilcPurpleBuddyRes r = context;
 	PurpleBuddy *b = r->b;
-	SilcClient client = r->client;
 	SilcClientEntry client_entry;
 	SilcAttributePayload attr;
 	SilcAttribute attribute;
 	SilcVCardStruct vcard;
-	SilcAttributeObjMime message, extension;
+	SilcMime message = NULL, extension = NULL;
 #ifdef SILC_ATTRIBUTE_USER_ICON
-	SilcAttributeObjMime usericon;
+	SilcMime usericon = NULL;
 #endif
 	SilcAttributeObjPk serverpk, usersign, serversign;
 	gboolean usign_success = TRUE, ssign_success = TRUE;
 	char filename[512], filename2[512], *fingerprint = NULL, *tmp;
 	SilcUInt32 len;
+	SilcHash hash;
 	int i;
 
 	if (!success) {
 		/* The user did not trust the public key. */
 		silcpurple_add_buddy_pk_no(r);
+		silc_free(r->offline_pk);
+		if (r->public_key)
+		  silc_pkcs_public_key_free(r->public_key);
 		silc_free(r);
 		return;
 	}
@@ -783,6 +774,8 @@
 		purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
 		silc_free(fingerprint);
 		silc_free(r->offline_pk);
+		if (r->public_key)
+		  silc_pkcs_public_key_free(r->public_key);
 		silc_free(r);
 		return;
 	}
@@ -791,16 +784,15 @@
 	client_entry = silc_client_get_client_by_id(r->client, r->conn,
 						    &r->client_id);
 	if (!client_entry) {
+		silc_free(r->offline_pk);
+		silc_pkcs_public_key_free(r->public_key);
+		if (r->public_key)
+		  silc_pkcs_public_key_free(r->public_key);
 		silc_free(r);
 		return;
 	}
 
 	memset(&vcard, 0, sizeof(vcard));
-	memset(&message, 0, sizeof(message));
-	memset(&extension, 0, sizeof(extension));
-#ifdef SILC_ATTRIBUTE_USER_ICON
-	memset(&usericon, 0, sizeof(usericon));
-#endif
 	memset(&serverpk, 0, sizeof(serverpk));
 	memset(&usersign, 0, sizeof(usersign));
 	memset(&serversign, 0, sizeof(serversign));
@@ -822,21 +814,24 @@
 				break;
 
 			case SILC_ATTRIBUTE_STATUS_MESSAGE:
-				if (!silc_attribute_get_object(attr, (void *)&message,
-							       sizeof(message)))
+				message = silc_mime_alloc();
+				if (!silc_attribute_get_object(attr, (void *)message,
+							       sizeof(*message)))
 					continue;
 				break;
 
 			case SILC_ATTRIBUTE_EXTENSION:
-				if (!silc_attribute_get_object(attr, (void *)&extension,
-							       sizeof(extension)))
+				extension = silc_mime_alloc();
+				if (!silc_attribute_get_object(attr, (void *)extension,
+							       sizeof(*extension)))
 					continue;
 				break;
 
 #ifdef SILC_ATTRIBUTE_USER_ICON
 			case SILC_ATTRIBUTE_USER_ICON:
-				if (!silc_attribute_get_object(attr, (void *)&usericon,
-							       sizeof(usericon)))
+				usericon = silc_mime_alloc();
+				if (!silc_attribute_get_object(attr, (void *)usericon,
+							       sizeof(*usericon)))
 					continue;
 				break;
 #endif
@@ -872,50 +867,54 @@
 	}
 
 	/* Verify the attribute signatures */
+	silc_hash_alloc((const unsigned char *)"sha1", &hash);
 
 	if (usersign.data) {
-		SilcPKCS pkcs;
 		unsigned char *verifyd;
 		SilcUInt32 verify_len;
 
-		silc_pkcs_alloc((unsigned char*)"rsa", &pkcs);
 		verifyd = silc_attribute_get_verify_data(client_entry->attrs,
 							 FALSE, &verify_len);
-		if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
-			if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
-							usersign.data,
-							usersign.data_len,
-							verifyd, verify_len))
-				usign_success = FALSE;
-		}
+		if (verifyd && !silc_pkcs_verify(client_entry->public_key,
+						 usersign.data,
+						 usersign.data_len,
+						 verifyd, verify_len, hash))
+			usign_success = FALSE;
 		silc_free(verifyd);
 	}
 
-	if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
+	if (serversign.data) {
 		SilcPublicKey public_key;
-		SilcPKCS pkcs;
+		SilcPKCSType type = 0;
 		unsigned char *verifyd;
 		SilcUInt32 verify_len;
 
-		if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
-						&public_key)) {
-			silc_pkcs_alloc((unsigned char *)"rsa", &pkcs);
+		if (!strcmp(serverpk.type, "silc-rsa"))
+		  type = SILC_PKCS_SILC;
+		else if (!strcmp(serverpk.type, "ssh-rsa"))
+		  type = SILC_PKCS_SSH2;
+		else if (!strcmp(serverpk.type, "x509v3-sign-rsa"))
+		  type = SILC_PKCS_X509V3;
+		else if (!strcmp(serverpk.type, "pgp-sign-rsa"))
+		  type = SILC_PKCS_OPENPGP;
+
+		if (silc_pkcs_public_key_alloc(type, serverpk.data,
+					       serverpk.data_len,
+					       &public_key)) {
 			verifyd = silc_attribute_get_verify_data(client_entry->attrs,
 								 TRUE, &verify_len);
-			if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
-				if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
-							       serversign.data,
-							       serversign.data_len,
-							       verifyd, verify_len))
-					ssign_success = FALSE;
-			}
+			if (verifyd && !silc_pkcs_verify(public_key,
+							 serversign.data,
+							 serversign.data_len,
+							 verifyd, verify_len,
+							 hash))
+				ssign_success = FALSE;
 			silc_pkcs_public_key_free(public_key);
 			silc_free(verifyd);
 		}
 	}
 
-	fingerprint = silc_fingerprint(client_entry->fingerprint,
-				       client_entry->fingerprint_len);
+	fingerprint = silc_fingerprint(client_entry->fingerprint, 20);
 	for (i = 0; i < strlen(fingerprint); i++)
 		if (fingerprint[i] == ' ')
 			fingerprint[i] = '_';
@@ -954,46 +953,45 @@
 		}
 
 		/* Save status message */
-		if (message.mime) {
+		if (message) {
 			memset(filename2, 0, sizeof(filename2));
 			g_snprintf(filename2, sizeof(filename2) - 1,
 				   "%s" G_DIR_SEPARATOR_S "status_message.mime",
 				   filename);
-			silc_file_writefile(filename2, (char *)message.mime,
-					    message.mime_len);
+			tmp = (char *)silc_mime_get_data(message, &len);
+			silc_file_writefile(filename2, tmp, len);
+			silc_mime_free(message);
 		}
 
 		/* Save extension data */
-		if (extension.mime) {
+		if (extension) {
 			memset(filename2, 0, sizeof(filename2));
 			g_snprintf(filename2, sizeof(filename2) - 1,
 				   "%s" G_DIR_SEPARATOR_S "extension.mime",
 				   filename);
-			silc_file_writefile(filename2, (char *)extension.mime,
-					    extension.mime_len);
+			tmp = (char *)silc_mime_get_data(extension, &len);
+			silc_file_writefile(filename2, tmp, len);
+			silc_mime_free(extension);
 		}
 
 #ifdef SILC_ATTRIBUTE_USER_ICON
 		/* Save user icon */
-		if (usericon.mime) {
-			SilcMime m = silc_mime_decode(usericon.mime,
-						      usericon.mime_len);
-			if (m) {
-				const char *type = silc_mime_get_field(m, "Content-Type");
-				if (!strcmp(type, "image/jpeg") ||
-				    !strcmp(type, "image/gif") ||
-				    !strcmp(type, "image/bmp") ||
-				    !strcmp(type, "image/png")) {
-					const unsigned char *data;
-					SilcUInt32 data_len;
-					data = silc_mime_get_data(m, &data_len);
-					if (data) {
-						/* TODO: Check if SILC gives us something to use as the checksum instead */
-						purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
-					}
+		if (usericon) {
+			const char *type = silc_mime_get_field(usericon, "Content-Type");
+			if (type &&
+			    (!strcmp(type, "image/jpeg") ||
+			     !strcmp(type, "image/gif") ||
+			     !strcmp(type, "image/bmp") ||
+			     !strcmp(type, "image/png"))) {
+				const unsigned char *data;
+				SilcUInt32 data_len;
+				data = silc_mime_get_data(usericon, &data_len);
+				if (data) {
+					/* TODO: Check if SILC gives us something to use as the checksum instead */
+					purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
 				}
-				silc_mime_free(m);
 			}
+			silc_mime_free(usericon);
 		}
 #endif
 	}
@@ -1015,7 +1013,11 @@
 	silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
 				 filename2, NULL);
 
+	silc_hash_free(hash);
 	silc_free(fingerprint);
+	silc_free(r->offline_pk);
+	if (r->public_key)
+	  silc_pkcs_public_key_free(r->public_key);
 	silc_free(r);
 }
 
@@ -1023,11 +1025,9 @@
 silcpurple_add_buddy_ask_import(void *user_data, const char *name)
 {
 	SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
-	SilcPublicKey public_key;
 
 	/* Load the public key */
-	if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
-	    !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
+	if (!silc_pkcs_load_public_key(name, &r->public_key)) {
 		silcpurple_add_buddy_ask_pk_cb(r, 0);
 		purple_notify_error(r->client->application,
 				  _("Add Buddy"), _("Could not load public key"), NULL);
@@ -1035,12 +1035,10 @@
 	}
 
 	/* Now verify the public key */
-	r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
+	r->offline_pk = silc_pkcs_public_key_encode(r->public_key, &r->offline_pk_len);
 	silcpurple_verify_public_key(r->client, r->conn, r->b->name,
-				   SILC_SOCKET_TYPE_CLIENT,
-				   r->offline_pk, r->offline_pk_len,
-				   SILC_SKE_PK_TYPE_SILC,
-				   silcpurple_add_buddy_save, r);
+				     SILC_CONN_CLIENT, r->public_key,
+				     silcpurple_add_buddy_save, r);
 }
 
 static void
@@ -1065,9 +1063,9 @@
 
 	/* Open file selector to select the public key. */
 	purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
-			  G_CALLBACK(silcpurple_add_buddy_ask_import),
-			  G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
-			  purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+			    G_CALLBACK(silcpurple_add_buddy_ask_import),
+			    G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
+			    purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
 
 }
 
@@ -1078,20 +1076,29 @@
 	g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
 		   r->b->name);
 	purple_request_action(r->client->application, _("Add Buddy"), tmp,
-			    _("To add the buddy you must import his/her public key. "
-			      "Press Import to import a public key."), 0,
-				  purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
-			    _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
-			    _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
+			      _("To add the buddy you must import his/her public key. "
+				"Press Import to import a public key."), 0,
+			      purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
+			      _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
+			      _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
 }
 
-static void
-silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
-			     SilcClientCommandReplyContext cmd)
+static SilcBool
+silcpurple_add_buddy_getkey_cb(SilcClient client, SilcClientConnection conn,
+			       SilcCommand command, SilcStatus status,
+			       SilcStatus error, void *context, va_list ap)
 {
+	SilcPurpleBuddyRes r = context;
 	SilcClientEntry client_entry;
-	unsigned char *pk;
-	SilcUInt32 pk_len;
+
+	if (status != SILC_STATUS_OK) {
+		/* The buddy is offline/nonexistent. We will require user
+		   to associate a public key with the buddy or the buddy
+		   cannot be added. */
+		r->offline = TRUE;
+		silcpurple_add_buddy_ask_pk(r);
+		return FALSE;
+	}
 
 	/* Get the client entry. */
 	client_entry = silc_client_get_client_by_id(r->client, r->conn,
@@ -1102,16 +1109,14 @@
 		   cannot be added. */
 		r->offline = TRUE;
 		silcpurple_add_buddy_ask_pk(r);
-		return;
+		return FALSE;
 	}
 
 	/* Now verify the public key */
-	pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
 	silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname,
-				   SILC_SOCKET_TYPE_CLIENT,
-				   pk, pk_len, SILC_SKE_PK_TYPE_SILC,
-				   silcpurple_add_buddy_save, r);
-	silc_free(pk);
+				     SILC_CONN_CLIENT, client_entry->public_key,
+				     silcpurple_add_buddy_save, r);
+	return TRUE;
 }
 
 static void
@@ -1120,6 +1125,7 @@
 	PurpleRequestField *f;
 	const GList *list;
 	SilcClientEntry client_entry;
+	SilcDList clients;
 
 	f = purple_request_fields_get_field(fields, "list");
 	list = purple_request_field_list_get_selected(f);
@@ -1131,7 +1137,11 @@
 	}
 
 	client_entry = purple_request_field_list_get_data(f, list->data);
-	silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
+	clients = silc_dlist_init();
+	silc_dlist_add(clients, client_entry);
+	silcpurple_add_buddy_resolved(r->client, r->conn, SILC_STATUS_OK,
+				      clients, r);
+	silc_dlist_uninit(clients);
 }
 
 static void
@@ -1143,16 +1153,14 @@
 }
 
 static void
-silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
-			  SilcClientEntry *clients,
-			  SilcUInt32 clients_count)
+silcpurple_add_buddy_select(SilcPurpleBuddyRes r, SilcDList clients)
 {
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *g;
 	PurpleRequestField *f;
 	char tmp[512], tmp2[128];
-	int i;
 	char *fingerprint;
+	SilcClientEntry client_entry;
 
 	fields = purple_request_fields_new();
 	g = purple_request_field_group_new(NULL);
@@ -1161,56 +1169,56 @@
 	purple_request_field_list_set_multi_select(f, FALSE);
 	purple_request_fields_add_group(fields, g);
 
-	for (i = 0; i < clients_count; i++) {
+	silc_dlist_start(clients);
+	while ((client_entry = silc_dlist_get(clients))) {
 		fingerprint = NULL;
-		if (clients[i]->fingerprint) {
-			fingerprint = silc_fingerprint(clients[i]->fingerprint,
-						       clients[i]->fingerprint_len);
+		if (*client_entry->fingerprint) {
+			fingerprint = silc_fingerprint(client_entry->fingerprint, 20);
 			g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
 		}
 		g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
-			   clients[i]->realname, clients[i]->nickname,
-			   clients[i]->username, clients[i]->hostname ?
-			   clients[i]->hostname : "",
+			   client_entry->realname, client_entry->nickname,
+			   client_entry->username, *client_entry->hostname ?
+			   client_entry->hostname : "",
 			   fingerprint ? tmp2 : "");
-		purple_request_field_list_add(f, tmp, clients[i]);
+		purple_request_field_list_add(f, tmp, client_entry);
 		silc_free(fingerprint);
 	}
 
 	purple_request_fields(r->client->application, _("Add Buddy"),
-				_("Select correct user"),
-				r->pubkey_search
-					? _("More than one user was found with the same public key. Select "
-						"the correct user from the list to add to the buddy list.")
-					: _("More than one user was found with the same name. Select "
-						"the correct user from the list to add to the buddy list."),
-				fields,
-				_("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
-				_("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
-				purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
+			      _("Select correct user"),
+			      r->pubkey_search
+			      ? _("More than one user was found with the same public key. Select "
+				  "the correct user from the list to add to the buddy list.")
+			      : _("More than one user was found with the same name. Select "
+				  "the correct user from the list to add to the buddy list."),
+			      fields,
+			      _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
+			      _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
+			      purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
 }
 
 static void
 silcpurple_add_buddy_resolved(SilcClient client,
-			    SilcClientConnection conn,
-			    SilcClientEntry *clients,
-			    SilcUInt32 clients_count,
-			    void *context)
+			      SilcClientConnection conn,
+			      SilcStatus status,
+			      SilcDList clients,
+			      void *context)
 {
 	SilcPurpleBuddyRes r = context;
 	PurpleBuddy *b = r->b;
 	SilcAttributePayload pub;
 	SilcAttributeObjPk userpk;
-	unsigned char *pk;
-	SilcUInt32 pk_len;
 	const char *filename;
+	SilcClientEntry client_entry = NULL;
+	SilcUInt16 cmd_ident;
 
 	filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
 
 	/* If the buddy is offline/nonexistent, we will require user
 	   to associate a public key with the buddy or the buddy
 	   cannot be added. */
-	if (!clients_count) {
+	if (!clients) {
 		if (r->init) {
 			silc_free(r);
 			return;
@@ -1228,33 +1236,37 @@
 
 	/* If more than one client was found with nickname, we need to verify
 	   from user which one is the correct. */
-	if (clients_count > 1 && !r->pubkey_search) {
+	if (silc_dlist_count(clients) > 1 && !r->pubkey_search) {
 		if (r->init) {
 			silc_free(r);
 			return;
 		}
 
-		silcpurple_add_buddy_select(r, clients, clients_count);
+		silcpurple_add_buddy_select(r, clients);
 		return;
 	}
 
+	silc_dlist_start(clients);
+	client_entry = silc_dlist_get(clients);
+
 	/* If we searched using public keys and more than one entry was found
 	   the same person is logged on multiple times. */
-	if (clients_count > 1 && r->pubkey_search && b->name) {
+	if (silc_dlist_count(clients) > 1 && r->pubkey_search && b->name) {
 		if (r->init) {
 			/* Find the entry that closest matches to the
 			   buddy nickname. */
-			int i;
-			for (i = 0; i < clients_count; i++) {
-				if (!strncasecmp(b->name, clients[i]->nickname,
+			SilcClientEntry entry;
+			silc_dlist_start(clients);
+			while ((entry = silc_dlist_get(clients))) {
+				if (!strncasecmp(b->name, entry->nickname,
 						 strlen(b->name))) {
-					clients[0] = clients[i];
+					client_entry = entry;
 					break;
 				}
 			}
 		} else {
 			/* Verify from user which one is correct */
-			silcpurple_add_buddy_select(r, clients, clients_count);
+			silcpurple_add_buddy_select(r, clients);
 			return;
 		}
 	}
@@ -1262,61 +1274,60 @@
 	/* The client was found.  Now get its public key and verify
 	   that before adding the buddy. */
 	memset(&userpk, 0, sizeof(userpk));
-	b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
-	r->client_id = *clients[0]->id;
+	b->proto_data = silc_memdup(&client_entry->id, sizeof(client_entry->id));
+	r->client_id = client_entry->id;
 
 	/* Get the public key from attributes, if not present then
 	   resolve it with GETKEY unless we have it cached already. */
-	if (clients[0]->attrs && !clients[0]->public_key) {
-		pub = silcpurple_get_attr(clients[0]->attrs,
-					SILC_ATTRIBUTE_USER_PUBLIC_KEY);
+	if (client_entry->attrs && !client_entry->public_key) {
+		pub = silcpurple_get_attr(client_entry->attrs,
+					  SILC_ATTRIBUTE_USER_PUBLIC_KEY);
 		if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
 						       sizeof(userpk))) {
 			/* Get public key with GETKEY */
-			silc_client_command_call(client, conn, NULL,
-						 "GETKEY", clients[0]->nickname, NULL);
+			cmd_ident =
+			  silc_client_command_call(client, conn, NULL,
+						   "GETKEY", client_entry->nickname, NULL);
 			silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
-						    conn->cmd_ident,
-						    (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+						    cmd_ident,
+						    silcpurple_add_buddy_getkey_cb,
 						    r);
 			return;
 		}
-		if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
-						 &clients[0]->public_key))
+		if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC,
+						userpk.data, userpk.data_len,
+						&client_entry->public_key))
 			return;
 		silc_free(userpk.data);
-	} else if (filename && !clients[0]->public_key) {
-		if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
-					       SILC_PKCS_FILE_PEM) &&
-		    !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
-					       SILC_PKCS_FILE_BIN)) {
+	} else if (filename && !client_entry->public_key) {
+		if (!silc_pkcs_load_public_key(filename, &client_entry->public_key)) {
 			/* Get public key with GETKEY */
-			silc_client_command_call(client, conn, NULL,
-						 "GETKEY", clients[0]->nickname, NULL);
+			cmd_ident =
+			  silc_client_command_call(client, conn, NULL,
+						   "GETKEY", client_entry->nickname, NULL);
 			silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
-						    conn->cmd_ident,
-						    (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+						    cmd_ident,
+						    silcpurple_add_buddy_getkey_cb,
 						    r);
 			return;
 		}
-	} else if (!clients[0]->public_key) {
+	} else if (!client_entry->public_key) {
 		/* Get public key with GETKEY */
-		silc_client_command_call(client, conn, NULL,
-					 "GETKEY", clients[0]->nickname, NULL);
+		cmd_ident =
+		  silc_client_command_call(client, conn, NULL,
+					   "GETKEY", client_entry->nickname, NULL);
 		silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
-					    conn->cmd_ident,
-					    (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
+					    cmd_ident,
+					    silcpurple_add_buddy_getkey_cb,
 					    r);
 		return;
 	}
 
 	/* We have the public key, verify it. */
-	pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
-	silcpurple_verify_public_key(client, conn, clients[0]->nickname,
-				   SILC_SOCKET_TYPE_CLIENT,
-				   pk, pk_len, SILC_SKE_PK_TYPE_SILC,
-				   silcpurple_add_buddy_save, r);
-	silc_free(pk);
+	silcpurple_verify_public_key(client, conn, client_entry->nickname,
+				     SILC_CONN_CLIENT,
+				     client_entry->public_key,
+				     silcpurple_add_buddy_save, r);
 }
 
 static void
@@ -1344,10 +1355,7 @@
 		SilcPublicKey public_key;
 		SilcAttributeObjPk userpk;
 
-		if (!silc_pkcs_load_public_key(filename, &public_key,
-					       SILC_PKCS_FILE_PEM) &&
-		    !silc_pkcs_load_public_key(filename, &public_key,
-					       SILC_PKCS_FILE_BIN))
+		if (!silc_pkcs_load_public_key(filename, &public_key))
 			return;
 
 		/* Get all attributes, and use the public key to search user */
@@ -1632,12 +1640,13 @@
 						    sg->conn,
 						    buddy->proto_data);
 
-	if (client_entry && client_entry->send_key) {
+	if (client_entry &&
+	    silc_client_private_message_key_is_set(sg->client,
+						   sg->conn, client_entry)) {
 		act = purple_menu_action_new(_("Reset IM Key"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_resetkey),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
-
 	} else {
 		act = purple_menu_action_new(_("IM with Key Exchange"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_keyagr),
@@ -1690,9 +1699,7 @@
 	SilcClientConnection conn = sg->conn;
 	SilcMime mime;
 	char type[32];
-	unsigned char *icon;
 	const char *t;
-	SilcAttributeObjMime obj;
 
 	/* Remove */
 	if (!img) {
@@ -1717,12 +1724,9 @@
 	silc_mime_add_field(mime, "Content-Type", type);
 	silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
 
-	obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
-	if (obj.mime)
-		silc_client_attribute_add(client, conn, 
-					  SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
+	silc_client_attribute_add(client, conn,
+				  SILC_ATTRIBUTE_USER_ICON, mime, sizeof(*mime));
 
-	silc_free(icon);
 	silc_mime_free(mime);
 }
 #endif
--- a/libpurple/protocols/silc/chat.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/chat.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 #include "wb.h"
@@ -61,10 +61,10 @@
 
 static void
 silcpurple_chat_getinfo_res(SilcClient client,
-			  SilcClientConnection conn,
-			  SilcChannelEntry *channels,
-			  SilcUInt32 channels_count,
-			  void *context)
+			    SilcClientConnection conn,
+			    SilcStatus status,
+			    SilcDList channels,
+			    void *context)
 {
 	GHashTable *components = context;
 	PurpleConnection *gc = client->application;
@@ -134,13 +134,14 @@
 	}
 	silc_hash_table_list_reset(&htl);
 
-	if (channel->channel_key)
+	if (channel->cipher)
 		g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
-				       silc_cipher_get_name(channel->channel_key));
+				       channel->cipher);
+
 	if (channel->hmac)
 		/* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
 		g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
-				       silc_hmac_get_name(channel->hmac));
+				       channel->hmac);
 
 	if (channel->topic) {
 		tmp2 = g_markup_escape_text(channel->topic, -1);
@@ -211,7 +212,7 @@
 	SilcPurple sg;
 	SilcChannelEntry channel;
 	PurpleChat *c;
-	SilcBuffer pubkeys;
+	SilcDList pubkeys;
 } *SilcPurpleChauth;
 
 static void
@@ -227,22 +228,21 @@
 	SilcUInt32 m;
 
 	/* Load the public key */
-	if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
-	    !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
+	if (!silc_pkcs_load_public_key(name, &public_key)) {
 		silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
-		silc_buffer_free(sgc->pubkeys);
+		silc_dlist_uninit(sgc->pubkeys);
 		silc_free(sgc);
 		purple_notify_error(client->application,
-				  _("Add Channel Public Key"),
-				  _("Could not load public key"), NULL);
+				    _("Add Channel Public Key"),
+				    _("Could not load public key"), NULL);
 		return;
 	}
 
-	pk = silc_pkcs_public_key_payload_encode(public_key);
+	pk = silc_public_key_payload_encode(public_key);
 	chpks = silc_buffer_alloc_size(2);
 	SILC_PUT16_MSB(1, chpks->head);
 	chpks = silc_argument_payload_encode_one(chpks, pk->data,
-						 pk->len, 0x00);
+						 silc_buffer_len(pk), 0x00);
 	silc_buffer_free(pk);
 
 	m = sgc->channel->mode;
@@ -250,15 +250,20 @@
 
 	/* Send CMODE */
 	SILC_PUT32_MSB(m, mode);
-	chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
+	chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
 	silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
-				 ++conn->cmd_ident, 3,
-				 1, chidp->data, chidp->len,
+				 silcpurple_command_reply, NULL, 3,
+				 1, chidp->data, silc_buffer_len(chidp),
 				 2, mode, sizeof(mode),
-				 9, chpks->data, chpks->len);
+				 9, chpks->data, silc_buffer_len(chpks));
 	silc_buffer_free(chpks);
 	silc_buffer_free(chidp);
-	silc_buffer_free(sgc->pubkeys);
+	if (sgc->pubkeys) {
+	  silc_dlist_start(sgc->pubkeys);
+	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
+		silc_pkcs_public_key_free(public_key);
+	  silc_dlist_uninit(sgc->pubkeys);
+	}
 	silc_free(sgc);
 }
 
@@ -266,8 +271,16 @@
 silcpurple_chat_chpk_cancel(void *user_data, const char *name)
 {
 	SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
+	SilcPublicKey public_key;
+
 	silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
-	silc_buffer_free(sgc->pubkeys);
+
+	if (sgc->pubkeys) {
+	  silc_dlist_start(sgc->pubkeys);
+	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
+		silc_pkcs_public_key_free(public_key);
+	  silc_dlist_uninit(sgc->pubkeys);
+	}
 	silc_free(sgc);
 }
 
@@ -289,9 +302,9 @@
 	if (!purple_request_field_list_get_selected(f)) {
 		/* Add new public key */
 		purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
-				  G_CALLBACK(silcpurple_chat_chpk_add),
-				  G_CALLBACK(silcpurple_chat_chpk_cancel),
-				  purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+				    G_CALLBACK(silcpurple_chat_chpk_add),
+				    G_CALLBACK(silcpurple_chat_chpk_cancel),
+				    purple_connection_get_account(sg->gc), NULL, NULL, sgc);
 		return;
 	}
 
@@ -302,13 +315,12 @@
 		public_key = purple_request_field_list_get_data(f, list->data);
 		if (purple_request_field_list_is_selected(f, list->data)) {
 			/* Delete this public key */
-			pk = silc_pkcs_public_key_payload_encode(public_key);
+			pk = silc_public_key_payload_encode(public_key);
 			chpks = silc_argument_payload_encode_one(chpks, pk->data,
-								 pk->len, 0x01);
+								 silc_buffer_len(pk), 0x01);
 			silc_buffer_free(pk);
 			c++;
 		}
-		silc_pkcs_public_key_free(public_key);
 	}
 	if (!c) {
 		silc_buffer_free(chpks);
@@ -322,15 +334,20 @@
 
 	/* Send CMODE */
 	SILC_PUT32_MSB(m, mode);
-	chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
+	chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
 	silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
-				 ++conn->cmd_ident, 3,
-				 1, chidp->data, chidp->len,
+				 silcpurple_command_reply, NULL, 3,
+				 1, chidp->data, silc_buffer_len(chidp),
 				 2, mode, sizeof(mode),
-				 9, chpks->data, chpks->len);
+				 9, chpks->data, silc_buffer_len(chpks));
 	silc_buffer_free(chpks);
 	silc_buffer_free(chidp);
-	silc_buffer_free(sgc->pubkeys);
+	if (sgc->pubkeys) {
+	  silc_dlist_start(sgc->pubkeys);
+	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
+		silc_pkcs_public_key_free(public_key);
+	  silc_dlist_uninit(sgc->pubkeys);
+	}
 	silc_free(sgc);
 }
 
@@ -339,6 +356,7 @@
 {
 	SilcPurple sg = sgc->sg;
 	PurpleRequestField *f;
+	SilcPublicKey public_key;
 	const char *curpass, *val;
 	int set;
 
@@ -365,19 +383,23 @@
 		purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
 	}
 
-	silc_buffer_free(sgc->pubkeys);
+	if (sgc->pubkeys) {
+	  silc_dlist_start(sgc->pubkeys);
+	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
+		silc_pkcs_public_key_free(public_key);
+	  silc_dlist_uninit(sgc->pubkeys);
+	}
 	silc_free(sgc);
 }
 
 void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
-			       SilcBuffer channel_pubkeys)
+				 SilcDList channel_pubkeys)
 {
-	SilcUInt16 argc;
-	SilcArgumentPayload chpks;
+	SilcPublicKey public_key;
+	SilcSILCPublicKey silc_pubkey;
 	unsigned char *pk;
-	SilcUInt32 pk_len, type;
+	SilcUInt32 pk_len;
 	char *fingerprint, *babbleprint;
-	SilcPublicKey pubkey;
 	SilcPublicKeyIdentifier ident;
 	char tmp2[1024], t[512];
 	PurpleRequestFields *fields;
@@ -399,7 +421,7 @@
 
 	g = purple_request_field_group_new(NULL);
 	f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
-					  curpass, FALSE);
+					    curpass, FALSE);
 	purple_request_field_string_set_masked(f, TRUE);
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
@@ -416,55 +438,49 @@
 		     "is required to be able to join. If channel public keys are set "
 		     "then only users whose public keys are listed are able to join."));
 
-	if (!channel_pubkeys) {
+	if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) {
 		f = purple_request_field_list_new("list", NULL);
 		purple_request_field_group_add_field(g, f);
 		purple_request_fields(sg->gc, _("Channel Authentication"),
-				    _("Channel Authentication"), t, fields,
-				    _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
-				    _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
-					purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+				      _("Channel Authentication"), t, fields,
+				      _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
+				      _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
+				      purple_connection_get_account(sg->gc), NULL, NULL, sgc);
+		if (channel_pubkeys)
+		  silc_dlist_uninit(channel_pubkeys);
 		return;
 	}
-	sgc->pubkeys = silc_buffer_copy(channel_pubkeys);
+	sgc->pubkeys = channel_pubkeys;
 
 	g = purple_request_field_group_new(NULL);
 	f = purple_request_field_list_new("list", NULL);
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
 
-	SILC_GET16_MSB(argc, channel_pubkeys->data);
-	chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
-					    channel_pubkeys->len - 2, argc);
-	if (!chpks)
-		return;
-
-	pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
-	while (pk) {
+	silc_dlist_start(channel_pubkeys);
+	while ((public_key = silc_dlist_get(channel_pubkeys))) {
+		pk = silc_pkcs_public_key_encode(public_key, &pk_len);
 		fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
 		babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
-		silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
-		ident = silc_pkcs_decode_identifier(pubkey->identifier);
+
+		silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+		ident = &silc_pubkey->identifier;
 
 		g_snprintf(tmp2, sizeof(tmp2), "%s\n  %s\n  %s",
 			   ident->realname ? ident->realname : ident->username ?
 			   ident->username : "", fingerprint, babbleprint);
-		purple_request_field_list_add(f, tmp2, pubkey);
+		purple_request_field_list_add(f, tmp2, public_key);
 
 		silc_free(fingerprint);
 		silc_free(babbleprint);
-		silc_pkcs_free_identifier(ident);
-		pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
 	}
 
 	purple_request_field_list_set_multi_select(f, FALSE);
 	purple_request_fields(sg->gc, _("Channel Authentication"),
-			    _("Channel Authentication"), t, fields,
-			    _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
-			    _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
-				purple_connection_get_account(sg->gc), NULL, NULL, sgc);
-
-	silc_argument_payload_free(chpks);
+			      _("Channel Authentication"), t, fields,
+			      _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
+			      _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
+			      purple_connection_get_account(sg->gc), NULL, NULL, sgc);
 }
 
 static void
@@ -525,9 +541,9 @@
 
 	/* Add private group to buddy list */
 	g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
-	comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp));
-	g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase));
+	comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+	g_hash_table_replace(comp, "channel", g_strdup(tmp));
+	g_hash_table_replace(comp, "passphrase", g_strdup(passphrase));
 
 	cn = purple_chat_new(sg->account, alias, comp);
 	g = (PurpleGroup *)p->c->node.parent;
@@ -596,9 +612,9 @@
 		   _("Please enter the %s channel private group name and passphrase."),
 		   p->channel);
 	purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
-			    _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
-			    _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
-				purple_connection_get_account(gc), NULL, NULL, p);
+			      _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
+			      _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
+			      purple_connection_get_account(gc), NULL, NULL, p);
 }
 
 
@@ -907,7 +923,7 @@
 		m = g_list_append(m, act);
 	}
 
-	if (mode & SILC_CHANNEL_UMODE_CHANFO) {
+	if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) {
 		act = purple_menu_action_new(_("Channel Authentication"),
 		                           PURPLE_CALLBACK(silcpurple_chat_chauth),
 		                           NULL, NULL);
@@ -926,7 +942,7 @@
 		}
 	}
 
-	if (mode & SILC_CHANNEL_UMODE_CHANOP) {
+	if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) {
 		act = purple_menu_action_new(_("Set User Limit"),
 		                           PURPLE_CALLBACK(silcpurple_chat_ulimit),
 		                           NULL, NULL);
@@ -969,7 +985,7 @@
 		}
 	}
 
-	if (channel) {
+	if (chu && channel) {
 		SilcPurpleChatWb wb;
 		wb = silc_calloc(1, sizeof(*wb));
 		wb->sg = sg;
@@ -986,86 +1002,10 @@
 
 /******************************* Joining Etc. ********************************/
 
-void silcpurple_chat_join_done(SilcClient client,
-			     SilcClientConnection conn,
-			     SilcClientEntry *clients,
-			     SilcUInt32 clients_count,
-			     void *context)
-{
-	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
-	SilcChannelEntry channel = context;
-	PurpleConversation *convo;
-	SilcUInt32 retry = SILC_PTR_TO_32(channel->context);
-	SilcHashTableList htl;
-	SilcChannelUser chu;
-	GList *users = NULL, *flags = NULL;
-	char tmp[256];
-
-	if (!clients && retry < 1) {
-		/* Resolving users failed, try again. */
-		channel->context = SILC_32_TO_PTR(retry + 1);
-		silc_client_get_clients_by_channel(client, conn, channel,
-						   silcpurple_chat_join_done, channel);
-		return;
-	}
-
-	/* Add channel to Purple */
-	channel->context = SILC_32_TO_PTR(++sg->channel_ids);
-	serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
-	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-							channel->channel_name, sg->account);
-	if (!convo)
-		return;
-
-	/* Add all users to channel */
-	silc_hash_table_list(channel->user_list, &htl);
-	while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
-		PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
-		if (!chu->client->nickname)
-			continue;
-		chu->context = SILC_32_TO_PTR(sg->channel_ids);
-
-		if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
-			f |= PURPLE_CBFLAGS_FOUNDER;
-		if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
-			f |= PURPLE_CBFLAGS_OP;
-		users = g_list_append(users, g_strdup(chu->client->nickname));
-		flags = g_list_append(flags, GINT_TO_POINTER(f));
-
-		if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
-			if (chu->client == conn->local_entry)
-				g_snprintf(tmp, sizeof(tmp),
-					   _("You are channel founder on <I>%s</I>"),
-					   channel->channel_name);
-			else
-				g_snprintf(tmp, sizeof(tmp),
-					   _("Channel founder on <I>%s</I> is <I>%s</I>"),
-					   channel->channel_name, chu->client->nickname);
-
-			purple_conversation_write(convo, NULL, tmp,
-						PURPLE_MESSAGE_SYSTEM, time(NULL));
-
-		}
-	}
-	silc_hash_table_list_reset(&htl);
-
-	purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
-	g_list_free(users);
-	g_list_free(flags);
-
-	/* Set topic */
-	if (channel->topic)
-		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
-
-	/* Set nick */
-	purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
-}
-
 char *silcpurple_get_chat_name(GHashTable *data)
 {
 	return g_strdup(g_hash_table_lookup(data, "channel"));
-}	
+}
 
 void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
 {
@@ -1073,6 +1013,7 @@
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
 	const char *channel, *passphrase, *parentch;
+	PurpleChat *chat;
 
 	if (!conn)
 		return;
@@ -1128,6 +1069,20 @@
 		return;
 	}
 
+	/* If the channel is not on buddy list, automatically add it there. */
+	chat = purple_blist_find_chat(sg->account, channel);
+	if (!chat) {
+		data = g_hash_table_new_full(g_str_hash, g_str_equal,
+					     g_free, g_free);
+		g_hash_table_replace(data, g_strdup("channel"),
+				     g_strdup(channel));
+		if (passphrase)
+		  g_hash_table_replace(data, g_strdup("passphrase"),
+				       g_strdup(passphrase));
+		chat = purple_chat_new(sg->account, NULL, data);
+		purple_blist_add_chat(chat, NULL, NULL);
+	}
+
 	/* XXX We should have other properties here as well:
 	   1. whether to try to authenticate to the channel
 	     1a. with default key,
@@ -1150,7 +1105,7 @@
 }
 
 void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
-			  const char *name)
+			    const char *name)
 {
 	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
@@ -1264,7 +1219,8 @@
 		}
 }
 
-int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags msgflags)
+int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
+			 PurpleMessageFlags msgflags)
 {
 	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
@@ -1274,10 +1230,13 @@
 	SilcChannelEntry channel = NULL;
 	SilcChannelPrivateKey key = NULL;
 	SilcUInt32 flags;
-	int ret;
+	int ret = 0;
 	char *msg2, *tmp;
 	gboolean found = FALSE;
 	gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
+#ifdef HAVE_SILCMIME_H
+	SilcDList list;
+#endif
 
 	if (!msg || !conn)
 		return 0;
@@ -1297,7 +1256,7 @@
 	} else if (strlen(msg) > 1 && msg[0] == '/') {
 		if (!silc_client_command_call(client, conn, msg + 1))
 			purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
-							  _("Unknown command"));
+					    _("Unknown command"));
 		g_free(tmp);
 		return 0;
 	}
@@ -1346,10 +1305,37 @@
 		channel = chu->channel;
 	}
 
+#ifdef HAVE_SILCMIME_H
+	/* Check for images */
+	if (msgflags & PURPLE_MESSAGE_IMAGES) {
+		list = silcpurple_image_message(msg, &flags);
+		if (list) {
+			/* Send one or more MIME message.  If more than one, they
+			   are MIME fragments due to over large message */
+			SilcBuffer buf;
+
+			silc_dlist_start(list);
+			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
+				ret =
+			 	silc_client_send_channel_message(client, conn,
+								 channel, key,
+								 flags, NULL,
+								 buf->data,
+								 silc_buffer_len(buf));
+			silc_mime_partial_free(list);
+			g_free(tmp);
+
+			if (ret)
+				  serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg, time(NULL));
+			return ret;
+		}
+	}
+#endif
+
 	/* Send channel message */
 	ret = silc_client_send_channel_message(client, conn, channel, key,
-					       flags, (unsigned char *)msg2,
-					       strlen(msg2), TRUE);
+					       flags, NULL, (unsigned char *)msg2,
+					       strlen(msg2));
 	if (ret) {
 		serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg,
 				 time(NULL));
--- a/libpurple/protocols/silc/ft.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/ft.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 
@@ -74,11 +74,23 @@
 	char tmp[256];
 
 	if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
+		/* All started sessions terminate here */
+		xfer->xfer->data = NULL;
 		purple_xfer_unref(xfer->xfer);
 		silc_free(xfer);
 		return;
 	}
 
+	if (status == SILC_CLIENT_FILE_MONITOR_DISCONNECT) {
+		purple_notify_error(gc, _("Secure File Transfer"),
+				    _("Error during file transfer"),
+				    _("Remote disconnected"));
+		xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE;
+		purple_xfer_update_progress(xfer->xfer);
+		silc_client_file_close(client, conn, session_id);
+		return;
+	}
+
 	if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT)
 		return;
 
@@ -96,17 +108,22 @@
 			purple_notify_error(gc, _("Secure File Transfer"),
 					  _("Error during file transfer"),
 					  _("Key agreement failed"));
+		} else if (error == SILC_CLIENT_FILE_TIMEOUT) {
+			purple_notify_error(gc, _("Secure File Transfer"),
+					  _("Error during file transfer"),
+					  _("Connection timedout"));
+		} else if (error == SILC_CLIENT_FILE_CONNECT_FAILED) {
+			purple_notify_error(gc, _("Secure File Transfer"),
+					  _("Error during file transfer"),
+					  _("Creating connection failed"));
 		} else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
 			purple_notify_error(gc, _("Secure File Transfer"),
 					  _("Error during file transfer"),
 					  _("File transfer session does not exist"));
-		} else {
-			purple_notify_error(gc, _("Secure File Transfer"),
-					  _("Error during file transfer"), NULL);
 		}
+		xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE;
+		purple_xfer_update_progress(xfer->xfer);
 		silc_client_file_close(client, conn, session_id);
-		purple_xfer_unref(xfer->xfer);
-		silc_free(xfer);
 		return;
 	}
 
@@ -133,6 +150,10 @@
 silcpurple_ftp_cancel(PurpleXfer *x)
 {
 	SilcPurpleXfer xfer = x->data;
+
+	if (!xfer)
+		return;
+
 	xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
 	purple_xfer_update_progress(xfer->xfer);
 	silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
@@ -143,6 +164,9 @@
 {
 	SilcPurpleXfer xfer = x->data;
 
+	if (!xfer)
+		return;
+
 	/* Cancel the transmission */
 	xfer->completion(NULL, xfer->completion_context);
 	silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
@@ -154,6 +178,9 @@
 	SilcPurpleXfer xfer = x->data;
 	const char *name;
 
+	if (!xfer)
+		return;
+
 	name = purple_xfer_get_local_filename(x);
 	g_unlink(name);
 	xfer->completion(name, xfer->completion_context);
@@ -187,17 +214,57 @@
 	SilcPurpleXfer xfer = x->data;
 	SilcClientFileError status;
 	PurpleConnection *gc = xfer->sg->gc;
+	SilcClientConnectionParams params;
+	gboolean local = xfer->hostname ? FALSE : TRUE;
+	char *local_ip = NULL, *remote_ip = NULL;
+	SilcSocket sock;
 
 	if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED)
 		return;
+	if (!xfer)
+		return;
+
+	silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream),
+				    &sock, NULL, NULL, NULL);
+
+	if (local) {
+		/* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
+		   to see if we are behind NAT. */
+		if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
+			/* Check if the IP is private */
+			if (silcpurple_ip_is_private(local_ip)) {
+				local = TRUE;
+				/* Local IP is private, resolve the remote server IP to see whether
+				   we are talking to Internet or just on LAN. */
+				if (silc_net_check_host_by_sock(sock, NULL,
+								&remote_ip))
+				  if (silcpurple_ip_is_private(remote_ip))
+				    /* We assume we are in LAN.  Let's provide the connection point. */
+				    local = TRUE;
+			}
+		}
+
+		if (local && !local_ip)
+		  local_ip = silc_net_localip();
+	}
+
+	memset(&params, 0, sizeof(params));
+	params.timeout_secs = 60;
+	if (local)
+	  /* Provide connection point */
+	  params.local_ip = local_ip;
 
 	/* Start the file transfer */
 	status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn,
+					  &params, xfer->sg->public_key,
+					  xfer->sg->private_key,
 					  silcpurple_ftp_monitor, xfer,
 					  NULL, xfer->session_id,
 					  silcpurple_ftp_ask_name, xfer);
 	switch (status) {
 	case SILC_CLIENT_FILE_OK:
+		silc_free(local_ip);
+		silc_free(remote_ip);
 		return;
 		break;
 
@@ -227,6 +294,8 @@
 	purple_xfer_unref(xfer->xfer);
 	g_free(xfer->hostname);
 	silc_free(xfer);
+	silc_free(local_ip);
+	silc_free(remote_ip);
 }
 
 static void
@@ -236,8 +305,8 @@
 }
 
 void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
-			  SilcClientEntry client_entry, SilcUInt32 session_id,
-			  const char *hostname, SilcUInt16 port)
+			    SilcClientEntry client_entry, SilcUInt32 session_id,
+			    const char *hostname, SilcUInt16 port)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
@@ -255,7 +324,7 @@
 	xfer->hostname = g_strdup(hostname);
 	xfer->port = port;
 	xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE,
-				   xfer->client_entry->nickname);
+				     xfer->client_entry->nickname);
 	if (!xfer->xfer) {
 		silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
 		g_free(xfer->hostname);
@@ -277,10 +346,12 @@
 silcpurple_ftp_send_cancel(PurpleXfer *x)
 {
 	SilcPurpleXfer xfer = x->data;
+
+	if (!xfer)
+		return;
+
+	/* This call will free all resources */
 	silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
-	purple_xfer_unref(xfer->xfer);
-	g_free(xfer->hostname);
-	silc_free(xfer);
 }
 
 static void
@@ -290,19 +361,26 @@
 	const char *name;
 	char *local_ip = NULL, *remote_ip = NULL;
 	gboolean local = TRUE;
+	SilcClientConnectionParams params;
+	SilcSocket sock;
+
+	if (!xfer)
+		return;
 
 	name = purple_xfer_get_local_filename(x);
 
+	silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream),
+				    &sock, NULL, NULL, NULL);
+
 	/* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
 	   to see if we are behind NAT. */
-	if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock,
-					 NULL, &local_ip)) {
+	if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
 		/* Check if the IP is private */
 		if (silcpurple_ip_is_private(local_ip)) {
 			local = FALSE;
 			/* Local IP is private, resolve the remote server IP to see whether
 			   we are talking to Internet or just on LAN. */
-			if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL,
+			if (silc_net_check_host_by_sock(sock, NULL,
 							&remote_ip))
 				if (silcpurple_ip_is_private(remote_ip))
 					/* We assume we are in LAN.  Let's provide the connection point. */
@@ -313,10 +391,17 @@
 	if (local && !local_ip)
 		local_ip = silc_net_localip();
 
+	memset(&params, 0, sizeof(params));
+	params.timeout_secs = 60;
+	if (local)
+	  /* Provide connection point */
+	  params.local_ip = local_ip;
+
 	/* Send the file */
 	silc_client_file_send(xfer->sg->client, xfer->sg->conn,
+			      xfer->client_entry, &params,
+			      xfer->sg->public_key, xfer->sg->private_key,
 			      silcpurple_ftp_monitor, xfer,
-			      local_ip, 0, !local, xfer->client_entry,
 			      name, &xfer->session_id);
 
 	silc_free(local_ip);
@@ -325,10 +410,10 @@
 
 static void
 silcpurple_ftp_send_file_resolved(SilcClient client,
-				SilcClientConnection conn,
-				SilcClientEntry *clients,
-				SilcUInt32 clients_count,
-				void *context)
+				  SilcClientConnection conn,
+				  SilcStatus status,
+				  SilcDList clients,
+				  void *context)
 {
 	PurpleConnection *gc = client->application;
 	char tmp[256];
@@ -352,38 +437,29 @@
 	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
-	SilcClientEntry *clients;
-	SilcUInt32 clients_count;
+	SilcDList clients;
 	SilcPurpleXfer xfer;
-	char *nickname;
 
 	g_return_val_if_fail(name != NULL, NULL);
 
-	if (!silc_parse_userfqdn(name, &nickname, NULL))
-		return NULL;
-
 	/* Find client entry */
-	clients = silc_client_get_clients_local(client, conn, nickname, name,
-											&clients_count);
+	clients = silc_client_get_clients_local(client, conn, name, FALSE);
 	if (!clients) {
-		silc_client_get_clients(client, conn, nickname, NULL,
-								silcpurple_ftp_send_file_resolved,
-								strdup(name));
-		silc_free(nickname);
+		silc_client_get_clients(client, conn, name, NULL,
+					silcpurple_ftp_send_file_resolved,
+					strdup(name));
 		return NULL;
 	}
+	silc_dlist_start(clients);
 
 	xfer = silc_calloc(1, sizeof(*xfer));
-
 	g_return_val_if_fail(xfer != NULL, NULL);
 
 	xfer->sg = sg;
-	xfer->client_entry = clients[0];
+	xfer->client_entry = silc_dlist_get(clients);
 	xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND,
-							   xfer->client_entry->nickname);
+				     xfer->client_entry->nickname);
 	if (!xfer->xfer) {
-		silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
-		g_free(xfer->hostname);
 		silc_free(xfer);
 		return NULL;
 	}
@@ -393,7 +469,6 @@
 	xfer->xfer->data = xfer;
 
 	silc_free(clients);
-	silc_free(nickname);
 
 	return xfer->xfer;
 }
--- a/libpurple/protocols/silc/ops.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/ops.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 #include "imgstore.h"
@@ -26,14 +26,18 @@
 static void
 silc_channel_message(SilcClient client, SilcClientConnection conn,
 		     SilcClientEntry sender, SilcChannelEntry channel,
-		     SilcMessagePayload payload, SilcChannelPrivateKey key,
-		     SilcMessageFlags flags, const unsigned char *message,
+		     SilcMessagePayload payload,
+		     SilcChannelPrivateKey key, SilcMessageFlags flags,
+		     const unsigned char *message,
 		     SilcUInt32 message_len);
 static void
 silc_private_message(SilcClient client, SilcClientConnection conn,
 		     SilcClientEntry sender, SilcMessagePayload payload,
 		     SilcMessageFlags flags, const unsigned char *message,
 		     SilcUInt32 message_len);
+static void
+silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+		    SilcAskPassphrase completion, void *context);
 
 /* Message sent to the application by library. `conn' associates the
    message to a specific connection.  `conn', however, may be NULL.
@@ -41,23 +45,32 @@
    The application can for example filter the message according the
    type. */
 
-static void
-silc_say(SilcClient client, SilcClientConnection conn,
-	 SilcClientMessageType type, char *msg, ...)
+void silc_say(SilcClient client, SilcClientConnection conn,
+	      SilcClientMessageType type, char *msg, ...)
 {
-	/* Nothing */
+	if (type == SILC_CLIENT_MESSAGE_ERROR) {
+		char tmp[256];
+		va_list va;
+
+		va_start(va, msg);
+		silc_vsnprintf(tmp, sizeof(tmp), msg, va);
+		purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp);
+
+		va_end(va);
+		return;
+	}
 }
 
 #ifdef HAVE_SILCMIME_H
 /* Processes incoming MIME message.  Can be private message or channel
-   message. */
+   message.  Returns TRUE if the message `mime' was displayed. */
 
-static void
+static SilcBool
 silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
-		      SilcClientEntry sender, SilcChannelEntry channel,
-		      SilcMessagePayload payload, SilcChannelPrivateKey key,
-		      SilcMessageFlags flags, SilcMime mime,
-		      gboolean recursive)
+			SilcClientEntry sender, SilcChannelEntry channel,
+			SilcMessagePayload payload, SilcChannelPrivateKey key,
+			SilcMessageFlags flags, SilcMime mime,
+			gboolean recursive)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
@@ -66,9 +79,10 @@
 	SilcUInt32 data_len;
 	PurpleMessageFlags cflags = 0;
 	PurpleConversation *convo = NULL;
+	SilcBool ret = FALSE;
 
 	if (!mime)
-		return;
+		return FALSE;
 
 	/* Check for fragmented MIME message */
 	if (silc_mime_is_partial(mime)) {
@@ -79,12 +93,12 @@
 		mime = silc_mime_assemble(sg->mimeass, mime);
 		if (!mime)
 			/* More fragments to come */
-			return;
+			return FALSE;
 
 		/* Process the complete message */
-		silcpurple_mime_message(client, conn, sender, channel,
-				      payload, key, flags, mime, FALSE);
-		return;
+		return silcpurple_mime_message(client, conn, sender, channel,
+					       payload, key, flags, mime,
+					       FALSE);
 	}
 
 	/* Check for multipart message */
@@ -92,17 +106,33 @@
 		SilcMime p;
 		const char *mtype;
 		SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
+		SilcBool ret;
 
-		/* Only "mixed" type supported */
-		if (strcmp(mtype, "mixed"))
-			goto out;
+		if (!strcmp(mtype, "mixed")) {
+			/* Contains multiple messages */
+			silc_dlist_start(parts);
+			while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
+			  /* Recursively process parts */
+			  ret = silcpurple_mime_message(client, conn, sender, channel,
+							payload, key, flags, p, TRUE);
+			}
+		}
 
-		silc_dlist_start(parts);
-		while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
-			/* Recursively process parts */
-			silcpurple_mime_message(client, conn, sender, channel,
-					      payload, key, flags, p, TRUE);
+		if (!strcmp(mtype, "alternative")) {
+			/* Same message in alternative formats.  Kopete sends
+			   these.  Go in order from last to first. */
+			silc_dlist_end(parts);
+			while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
+			  /* Go through the alternatives and display the first
+			     one we support. */
+			  if (silcpurple_mime_message(client, conn, sender, channel,
+						      payload, key, flags, p, TRUE)) {
+			    ret = TRUE;
+			    break;
+			  }
+			}
 		}
+
 		goto out;
 	}
 
@@ -124,13 +154,14 @@
 
 		if (channel)
 			silc_channel_message(client, conn, sender, channel,
-					     payload, key, 
+					     payload, key,
 					     SILC_MESSAGE_FLAG_UTF8, data,
 					     data_len);
 		else
 			silc_private_message(client, conn, sender, payload,
 					     SILC_MESSAGE_FLAG_UTF8, data,
 					     data_len);
+		ret = TRUE;
 		goto out;
 	}
 
@@ -157,7 +188,7 @@
 		}
 		if (channel && !convo)
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-								    channel->channel_name, sg->account);
+								      channel->channel_name, sg->account);
 		if (channel && !convo)
 			goto out;
 
@@ -165,11 +196,11 @@
 		if (imgid) {
 			cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
 			g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
-		  
+
 			if (channel)
 				serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
 				 		 sender->nickname ?
-				 		  sender->nickname : 
+				 		  sender->nickname :
 						 "<unknown>", cflags,
 						 tmp, time(NULL));
 			else
@@ -179,6 +210,7 @@
 
 			purple_imgstore_unref_by_id(imgid);
 			cflags = 0;
+			ret = TRUE;
 		}
 		goto out;
 	}
@@ -191,13 +223,15 @@
 					       payload, flags, data, data_len);
 		else
 			silcpurple_wb_receive(client, conn, sender, payload,
-					    flags, data, data_len);
+					      flags, data, data_len);
+		ret = TRUE;
 		goto out;
 	}
 
  out:
 	if (!recursive)
 		silc_mime_free(mime);
+	return ret;
 }
 #endif /* HAVE_SILCMIME_H */
 
@@ -210,8 +244,9 @@
 static void
 silc_channel_message(SilcClient client, SilcClientConnection conn,
 		     SilcClientEntry sender, SilcChannelEntry channel,
-		     SilcMessagePayload payload, SilcChannelPrivateKey key,
-		     SilcMessageFlags flags, const unsigned char *message,
+		     SilcMessagePayload payload,
+		     SilcChannelPrivateKey key, SilcMessageFlags flags,
+		     const unsigned char *message,
 		     SilcUInt32 message_len)
 {
 	PurpleConnection *gc = client->application;
@@ -236,7 +271,7 @@
 	}
 	if (!convo)
 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-								channel->channel_name, sg->account);
+							      channel->channel_name, sg->account);
 	if (!convo)
 		return;
 
@@ -249,9 +284,9 @@
 		/* Process MIME message */
 #ifdef HAVE_SILCMIME_H
 		SilcMime mime;
-		mime = silc_mime_decode(message, message_len);
+		mime = silc_mime_decode(NULL, message, message_len);
 		silcpurple_mime_message(client, conn, sender, channel, payload,
-				      key, flags, mime, FALSE);
+					key, flags, mime, FALSE);
 #else
 		char type[128], enc[128];
 		unsigned char *data;
@@ -283,9 +318,7 @@
 		tmp = g_markup_escape_text(msg, -1);
 		/* Send to Purple */
 		serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
-				 sender->nickname ?
-				 sender->nickname : "<unknown>", 0,
-				 tmp, time(NULL));
+				 sender->nickname, 0, tmp, time(NULL));
 		g_free(tmp);
 		g_free(msg);
 		return;
@@ -293,9 +326,7 @@
 
 	if (flags & SILC_MESSAGE_FLAG_NOTICE) {
 		msg = g_strdup_printf("(notice) <I>%s</I> %s",
-				      sender->nickname ?
-				      sender->nickname : "<unknown>",
-				      (const char *)message);
+				      sender->nickname, (const char *)message);
 		if (!msg)
 			return;
 
@@ -310,9 +341,7 @@
 		tmp = g_markup_escape_text((const char *)message, -1);
 		/* Send to Purple */
 		serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
-				 sender->nickname ?
-				 sender->nickname : "<unknown>", 0,
-				 tmp, time(NULL));
+				 sender->nickname, 0, tmp, time(NULL));
 		g_free(tmp);
 	}
 }
@@ -341,7 +370,7 @@
 	if (sender->nickname)
 		/* XXX - Should this be PURPLE_CONV_TYPE_IM? */
 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
-								sender->nickname, sg->account);
+							      sender->nickname, sg->account);
 
 	if (flags & SILC_MESSAGE_FLAG_SIGNED &&
 	    purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
@@ -352,7 +381,7 @@
 #ifdef HAVE_SILCMIME_H
 		/* Process MIME message */
 		SilcMime mime;
-		mime = silc_mime_decode(message, message_len);
+		mime = silc_mime_decode(NULL, message, message_len);
 		silcpurple_mime_message(client, conn, sender, NULL, payload,
 				      NULL, flags, mime, FALSE);
 #else
@@ -364,7 +393,7 @@
 		memset(enc, 0, sizeof(enc));
 
 		if (!silc_mime_parse(message, message_len, NULL, 0,
-		    type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data, 
+		    type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
 		    &data_len))
 			return;
 
@@ -383,11 +412,9 @@
 		if (!msg)
 			return;
 
+		/* Send to Purple */
 		tmp = g_markup_escape_text(msg, -1);
-		/* Send to Purple */
-		serv_got_im(gc, sender->nickname ?
-			    sender->nickname : "<unknown>",
-			    tmp, 0, time(NULL));
+		serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
 		g_free(msg);
 		g_free(tmp);
 		return;
@@ -395,15 +422,13 @@
 
 	if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
 		msg = g_strdup_printf("(notice) <I>%s</I> %s",
-				      sender->nickname ?
-				      sender->nickname : "<unknown>",
-				      (const char *)message);
+				      sender->nickname, (const char *)message);
 		if (!msg)
 			return;
 
 		/* Send to Purple */
 		purple_conversation_write(convo, NULL, (const char *)msg,
-					PURPLE_MESSAGE_SYSTEM, time(NULL));
+					  PURPLE_MESSAGE_SYSTEM, time(NULL));
 		g_free(msg);
 		return;
 	}
@@ -411,9 +436,7 @@
 	if (flags & SILC_MESSAGE_FLAG_UTF8) {
 		tmp = g_markup_escape_text((const char *)message, -1);
 		/* Send to Purple */
-		serv_got_im(gc, sender->nickname ?
-			    sender->nickname : "<unknown>",
-			    tmp, 0, time(NULL));
+		serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
 		g_free(tmp);
 	}
 }
@@ -447,6 +470,7 @@
 	char buf[512], buf2[512], *tmp, *name;
 	SilcNotifyType notify;
 	PurpleBuddy *b;
+	SilcDList list;
 	int i;
 
 	va_start(va, type);
@@ -460,7 +484,7 @@
 	case SILC_NOTIFY_TYPE_INVITE:
 		{
 			GHashTable *components;
-			va_arg(va, SilcChannelEntry);
+			(void)va_arg(va, SilcChannelEntry);
 			name = va_arg(va, char *);
 			client_entry = va_arg(va, SilcClientEntry);
 
@@ -479,7 +503,7 @@
 			break;
 
 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-								channel->channel_name, sg->account);
+							      channel->channel_name, sg->account);
 		if (!convo)
 			break;
 
@@ -487,7 +511,7 @@
 		g_snprintf(buf, sizeof(buf), "%s@%s",
 			   client_entry->username, client_entry->hostname);
 		purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
-					g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
+					  g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
 
 		break;
 
@@ -496,13 +520,13 @@
 		channel = va_arg(va, SilcChannelEntry);
 
 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-								channel->channel_name, sg->account);
+							      channel->channel_name, sg->account);
 		if (!convo)
 			break;
 
 		/* Remove user from channel */
 		purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
-					   client_entry->nickname, NULL);
+					     client_entry->nickname, NULL);
 
 		break;
 
@@ -510,19 +534,16 @@
 		client_entry = va_arg(va, SilcClientEntry);
 		tmp = va_arg(va, char *);
 
-		if (!client_entry->nickname)
-			break;
-
 		/* Remove from all channels */
 		silc_hash_table_list(client_entry->channels, &htl);
 		while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-									chu->channel->channel_name, sg->account);
+								      chu->channel->channel_name, sg->account);
 			if (!convo)
 				continue;
 			purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
-						   client_entry->nickname,
-						   tmp);
+						     client_entry->nickname,
+						     tmp);
 		}
 		silc_hash_table_list_reset(&htl);
 
@@ -537,7 +558,7 @@
 			channel = va_arg(va, SilcChannelEntry);
 
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-									channel->channel_name, sg->account);
+								      channel->channel_name, sg->account);
 			if (!convo)
 				break;
 
@@ -586,22 +607,22 @@
 		}
 	case SILC_NOTIFY_TYPE_NICK_CHANGE:
 		client_entry = va_arg(va, SilcClientEntry);
-		client_entry2 = va_arg(va, SilcClientEntry);
+		tmp = va_arg(va, char *);      /* Old nick */
+		name = va_arg(va, char *);     /* New nick */
 
-		if (!strcmp(client_entry->nickname, client_entry2->nickname))
+		if (!strcmp(tmp, name))
 			break;
 
 		/* Change nick on all channels */
-		silc_hash_table_list(client_entry2->channels, &htl);
+		silc_hash_table_list(client_entry->channels, &htl);
 		while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-									chu->channel->channel_name, sg->account);
+								      chu->channel->channel_name, sg->account);
 			if (!convo)
 				continue;
 			if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
 				purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
-										   client_entry->nickname,
-										   client_entry2->nickname);
+							     tmp, name);
 		}
 		silc_hash_table_list_reset(&htl);
 
@@ -615,11 +636,11 @@
 		(void)va_arg(va, char *);
 		(void)va_arg(va, char *);
 		(void)va_arg(va, SilcPublicKey);
-		(void)va_arg(va, SilcBuffer);
+		(void)va_arg(va, SilcDList);
 		channel = va_arg(va, SilcChannelEntry);
 
 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-								channel->channel_name, sg->account);
+							      channel->channel_name, sg->account);
 		if (!convo)
 			break;
 
@@ -643,7 +664,7 @@
 				   channel->channel_name);
 		}
 		purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
-				     buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+				       buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
 		break;
 
 	case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
@@ -656,7 +677,7 @@
 			channel = va_arg(va, SilcChannelEntry);
 
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-									channel->channel_name, sg->account);
+								      channel->channel_name, sg->account);
 			if (!convo)
 				break;
 
@@ -672,19 +693,19 @@
 			if (mode) {
 				silcpurple_get_chumode_string(mode, buf2, sizeof(buf2));
 				g_snprintf(buf, sizeof(buf),
-						_("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
-						client_entry2->nickname, buf2);
+					   _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
+					   client_entry2->nickname, buf2);
 				if (mode & SILC_CHANNEL_UMODE_CHANFO)
 					flags |= PURPLE_CBFLAGS_FOUNDER;
 				if (mode & SILC_CHANNEL_UMODE_CHANOP)
 					flags |= PURPLE_CBFLAGS_OP;
 			} else {
 				g_snprintf(buf, sizeof(buf),
-						_("<I>%s</I> removed all <I>%s's</I> modes"), name,
-						client_entry2->nickname);
+					   _("<I>%s</I> removed all <I>%s's</I> modes"), name,
+					   client_entry2->nickname);
 			}
 			purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
-					buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+					       buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
 			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
 			break;
 		}
@@ -702,7 +723,7 @@
 		channel = va_arg(va, SilcChannelEntry);
 
 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-								channel->channel_name, sg->account);
+							      channel->channel_name, sg->account);
 		if (!convo)
 			break;
 
@@ -713,15 +734,15 @@
 				   channel->channel_name, client_entry2->nickname,
 				   tmp ? tmp : "");
 			purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
-					     buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+					       buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
 			serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
 		} else {
 			/* Remove user from channel */
 			g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
 				   client_entry2->nickname, tmp ? tmp : "");
 			purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
-						   client_entry->nickname,
-						   buf);
+						     client_entry->nickname,
+						     buf);
 		}
 
 		break;
@@ -732,9 +753,6 @@
 		idtype = va_arg(va, int);
 		entry = va_arg(va, SilcClientEntry);
 
-		if (!client_entry->nickname)
-			break;
-
 		if (client_entry == conn->local_entry) {
 			if (idtype == SILC_ID_CLIENT) {
 				client_entry2 = (SilcClientEntry)entry;
@@ -761,7 +779,7 @@
 				if (!convo)
 					continue;
 				purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
-						     buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
+						       buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
 				serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
 			}
 			silc_hash_table_list_reset(&htl);
@@ -792,7 +810,7 @@
 				if (!convo)
 					continue;
 				purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
-							   client_entry->nickname, tmp);
+							     client_entry->nickname, tmp);
 			}
 			silc_hash_table_list_reset(&htl);
 		}
@@ -803,33 +821,23 @@
 		break;
 
 	case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
-		{
-			int i;
-			SilcClientEntry *clients;
-			SilcUInt32 clients_count;
-
-			(void)va_arg(va, void *);
-			clients = va_arg(va, SilcClientEntry *);
-			clients_count = va_arg(va, SilcUInt32);
-
-			for (i = 0; i < clients_count; i++) {
-				if (!clients[i]->nickname)
-					break;
+		(void)va_arg(va, void *);
+		list = va_arg(va, SilcDList);
 
-				/* Remove from all channels */
-				silc_hash_table_list(clients[i]->channels, &htl);
-				while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
-					convo =
-						purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-											chu->channel->channel_name, sg->account);
-					if (!convo)
-						continue;
-					purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
-								   clients[i]->nickname,
-								   _("Server signoff"));
-				}
-				silc_hash_table_list_reset(&htl);
+		silc_dlist_start(list);
+		while ((client_entry = silc_dlist_get(list))) {
+			/* Remove from all channels */
+			silc_hash_table_list(client_entry->channels, &htl);
+			while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+				convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+									      chu->channel->channel_name, sg->account);
+				if (!convo)
+					continue;
+				purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
+							     client_entry->nickname,
+							     _("Server signoff"));
 			}
+			silc_hash_table_list_reset(&htl);
 		}
 		break;
 
@@ -837,8 +845,8 @@
 		{
 			SilcStatus error = va_arg(va, int);
 			purple_notify_error(gc, "Error Notify",
-					  silc_get_status_message(error),
-					  NULL);
+					    silc_get_status_message(error),
+					    NULL);
 		}
 		break;
 
@@ -909,8 +917,8 @@
 			}
 
 			silc_free(b->proto_data);
-			b->proto_data = silc_memdup(client_entry->id,
-						    sizeof(*client_entry->id));
+			b->proto_data = silc_memdup(&client_entry->id,
+						    sizeof(client_entry->id));
 			if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
 				break;
 			} else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
@@ -955,19 +963,20 @@
 }
 
 
-/* Command handler. This function is called always in the command function.
-   If error occurs it will be called as well. `conn' is the associated
-   client connection. `cmd_context' is the command context that was
-   originally sent to the command. `success' is FALSE if error occurred
-   during command. `command' is the command being processed. It must be
-   noted that this is not reply from server. This is merely called just
-   after application has called the command. Just to tell application
-   that the command really was processed. */
+/* Command handler. This function is called always after application has
+   called a command.  It will be called to indicate that the command
+   was processed.  It will also be called if error occurs while processing
+   the command.  The `success' indicates whether the command was sent
+   or if error occurred.  The `status' indicates the actual error.
+   The `argc' and `argv' are the command line arguments sent to the
+   command by application.  Note that, this is not reply to the command
+   from server, this is merely and indication to application that the
+   command was processed. */
 
 static void
 silc_command(SilcClient client, SilcClientConnection conn,
-	     SilcClientCommandContext cmd_context, bool success,
-	     SilcCommand command, SilcStatus status)
+	     SilcBool success, SilcCommand command, SilcStatus status,
+	     SilcUInt32 argc, unsigned char **argv)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
@@ -975,8 +984,7 @@
 	switch (command) {
 
 	case SILC_COMMAND_CMODE:
-		if (cmd_context->argc == 3 &&
-		    !strcmp((char *)cmd_context->argv[2], "+C"))
+		if (argc == 3 && !strcmp((char *)argv[2], "+C"))
 			sg->chpk = TRUE;
 		else
 			sg->chpk = FALSE;
@@ -1090,53 +1098,96 @@
 }
 #endif
 
-/* Command reply handler. This function is called always in the command reply
-   function. If error occurs it will be called as well. Normal scenario
-   is that it will be called after the received command data has been parsed
-   and processed. The function is used to pass the received command data to
-   the application.
 
-   `conn' is the associated client connection. `cmd_payload' is the command
-   payload data received from server and it can be ignored. It is provided
-   if the application would like to re-parse the received command data,
-   however, it must be noted that the data is parsed already by the library
-   thus the payload can be ignored. `success' is FALSE if error occurred.
-   In this case arguments are not sent to the application. The `status' is
-   the command reply status server returned. The `command' is the command
-   reply being processed. The function has variable argument list and each
-   command defines the number and type of arguments it passes to the
-   application (on error they are not sent). */
+/* Command reply handler.  Delivers a reply to command that was sent
+   earlier.  The `conn' is the associated client connection.  The `command'
+   indicates the command reply type.  If the `status' other than
+   SILC_STATUS_OK an error occurred.  In this case the `error' will indicate
+   the error.  It is possible to receive list of command replies and list
+   of errors.  In this case the `status' will indicate it is an list entry
+   (the `status' is SILC_STATUS_LIST_START, SILC_STATUS_LIST_ITEM and/or
+   SILC_STATUS_LIST_END).
+
+   The arguments received in `ap' are command specific.  See a separate
+   documentation in the Toolkit Reference Manual for the command reply
+   arguments. */
 
 static void
 silc_command_reply(SilcClient client, SilcClientConnection conn,
-		   SilcCommandPayload cmd_payload, bool success,
-		   SilcCommand command, SilcStatus status, ...)
+		   SilcCommand command, SilcStatus status,
+		   SilcStatus error, va_list ap)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
 	PurpleConversation *convo;
-	va_list vp;
-
-	va_start(vp, status);
 
 	switch (command) {
 	case SILC_COMMAND_JOIN:
 		{
-			SilcChannelEntry channel_entry;
+			SilcChannelEntry channel;
+			PurpleConversation *convo;
+			SilcHashTableList *user_list;
+			SilcChannelUser chu;
+			GList *users = NULL, *flags = NULL;
+			char tmp[256], *topic;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
-						  silc_get_status_message(status));
+						    silc_get_status_message(error));
 				return;
 			}
 
-			(void)va_arg(vp, char *);
-			channel_entry = va_arg(vp, SilcChannelEntry);
+			(void)va_arg(ap, char *);
+			channel = va_arg(ap, SilcChannelEntry);
+			(void)va_arg(ap, SilcUInt32);
+			user_list = va_arg(ap, SilcHashTableList *);
+			topic = va_arg(ap, char *);
+
+			/* Add channel to Purple */
+			channel->context = SILC_32_TO_PTR(++sg->channel_ids);
+			serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
+			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+								      channel->channel_name, sg->account);
+			if (!convo)
+			  return;
+
+			/* Add all users to channel */
+			while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
+			  PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
+			  chu->context = SILC_32_TO_PTR(sg->channel_ids);
+
+			  if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+			    f |= PURPLE_CBFLAGS_FOUNDER;
+			  if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
+			    f |= PURPLE_CBFLAGS_OP;
+			  users = g_list_append(users, g_strdup(chu->client->nickname));
+			  flags = g_list_append(flags, GINT_TO_POINTER(f));
 
-			/* Resolve users on channel */
-			silc_client_get_clients_by_channel(client, conn, channel_entry,
-							   silcpurple_chat_join_done,
-							   channel_entry);
+			  if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
+			    if (chu->client == conn->local_entry)
+				g_snprintf(tmp, sizeof(tmp),
+					   _("You are channel founder on <I>%s</I>"),
+					   channel->channel_name);
+			    else
+				g_snprintf(tmp, sizeof(tmp),
+					   _("Channel founder on <I>%s</I> is <I>%s</I>"),
+					   channel->channel_name, chu->client->nickname);
+
+			    purple_conversation_write(convo, NULL, tmp,
+						      PURPLE_MESSAGE_SYSTEM, time(NULL));
+			  }
+			}
+
+			purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
+			g_list_free(users);
+			g_list_free(flags);
+
+			/* Set topic */
+			if (topic)
+			  purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic);
+
+			/* Set nick */
+			purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
 		}
 		break;
 
@@ -1148,31 +1199,29 @@
 
 	case SILC_COMMAND_WHOIS:
 		{
-			SilcUInt32 idle, mode;
-			SilcBuffer channels, user_modes;
+			SilcUInt32 idle, *user_modes;
+			SilcDList channels;
 			SilcClientEntry client_entry;
 			char tmp[1024], *tmp2;
 			char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
 			PurpleNotifyUserInfo *user_info;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("User Information"),
 						_("Cannot get user information"),
-						silc_get_status_message(status));
+						silc_get_status_message(error));
 				break;
 			}
 
-			client_entry = va_arg(vp, SilcClientEntry);
-			if (!client_entry->nickname)
-				break;
-			(void)va_arg(vp, char *);
-			(void)va_arg(vp, char *);
-			(void)va_arg(vp, char *);
-			channels = va_arg(vp, SilcBuffer);
-			mode = va_arg(vp, SilcUInt32);
-			idle = va_arg(vp, SilcUInt32);
-			(void)va_arg(vp, unsigned char *);
-			user_modes = va_arg(vp, SilcBuffer);
+			client_entry = va_arg(ap, SilcClientEntry);
+			(void)va_arg(ap, char *);
+			(void)va_arg(ap, char *);
+			(void)va_arg(ap, char *);
+			channels = va_arg(ap, SilcDList);
+			(void)va_arg(ap, SilcUInt32);
+			idle = va_arg(ap, SilcUInt32);
+			(void)va_arg(ap, unsigned char *);
+			user_modes = va_arg(ap, SilcUInt32 *);
 
 			user_info = purple_notify_user_info_new();
 			tmp2 = g_markup_escape_text(client_entry->nickname, -1);
@@ -1183,22 +1232,20 @@
 				purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
 				g_free(tmp2);
 			}
-			if (client_entry->username) {
-				tmp2 = g_markup_escape_text(client_entry->username, -1);
-				if (client_entry->hostname) {
-					gchar *tmp3;
-					tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
-					purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
-					g_free(tmp3);
-				} else
-					purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
-				g_free(tmp2);
-			}
+			tmp2 = g_markup_escape_text(client_entry->username, -1);
+			if (*client_entry->hostname) {
+				gchar *tmp3;
+				tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
+				purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
+				g_free(tmp3);
+			} else
+				purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
+			g_free(tmp2);
 
 			if (client_entry->mode) {
 				memset(tmp, 0, sizeof(tmp));
 				silcpurple_get_umode_string(client_entry->mode,
-						tmp, sizeof(tmp) - strlen(tmp));
+							    tmp, sizeof(tmp) - strlen(tmp));
 				purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
 			}
 
@@ -1240,39 +1287,28 @@
 				g_free(geostr);
 			}
 
-			if (client_entry->server)
+			if (*client_entry->server)
 				purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
 
 			if (channels && user_modes) {
-				SilcUInt32 *umodes;
-				SilcDList list =
-					silc_channel_payload_parse_list(channels->data,
-							channels->len);
-				if (list && silc_get_mode_list(user_modes,
-							silc_dlist_count(list),
-							&umodes)) {
-					SilcChannelPayload entry;
-					int i = 0;
+				SilcChannelPayload entry;
+				int i = 0;
 
-					memset(tmp, 0, sizeof(tmp));
-					silc_dlist_start(list);
-					while ((entry = silc_dlist_get(list))
-							!= SILC_LIST_END) {
-						SilcUInt32 name_len;
-						char *m = silc_client_chumode_char(umodes[i++]);
-						char *name = (char *)silc_channel_get_name(entry, &name_len);
-						if (m)
-							silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
-						silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
-						silc_strncat(tmp, sizeof(tmp) - 1, "  ", 1);
-						silc_free(m);
-
-					}
-					tmp2 = g_markup_escape_text(tmp, -1);
-					purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
-					g_free(tmp2);
-					silc_free(umodes);
+				memset(tmp, 0, sizeof(tmp));
+				silc_dlist_start(channels);
+				while ((entry = silc_dlist_get(channels))) {
+					SilcUInt32 name_len;
+					char *m = silc_client_chumode_char(user_modes[i++]);
+					char *name = (char *)silc_channel_get_name(entry, &name_len);
+					if (m)
+						silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
+					silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
+					silc_strncat(tmp, sizeof(tmp) - 1, "  ", 1);
+					silc_free(m);
 				}
+				tmp2 = g_markup_escape_text(tmp, -1);
+				purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
+				g_free(tmp2);
 			}
 
 			if (client_entry->public_key) {
@@ -1297,7 +1333,7 @@
 						_("OK"), G_CALLBACK(silcpurple_whois_more),
 						_("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
 			else
-#endif
+#endif /* 0 */
 			purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
 			purple_notify_user_info_destroy(user_info);
 		}
@@ -1309,17 +1345,17 @@
 			char *nickname, *realname, *username, *tmp;
 			PurpleNotifyUserInfo *user_info;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("User Information"),
 						  _("Cannot get user information"),
-						  silc_get_status_message(status));
+						  silc_get_status_message(error));
 				break;
 			}
 
-			client_entry = va_arg(vp, SilcClientEntry);
-			nickname = va_arg(vp, char *);
-			username = va_arg(vp, char *);
-			realname = va_arg(vp, char *);
+			client_entry = va_arg(ap, SilcClientEntry);
+			nickname = va_arg(ap, char *);
+			username = va_arg(ap, char *);
+			realname = va_arg(ap, char *);
 			if (!nickname)
 				break;
 
@@ -1334,7 +1370,7 @@
 			}
 			if (username) {
 				tmp = g_markup_escape_text(username, -1);
-				if (client_entry && client_entry->hostname) {
+				if (client_entry && *client_entry->hostname) {
 					gchar *tmp3;
 					tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
 					purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
@@ -1343,7 +1379,7 @@
 					purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
 				g_free(tmp);
 			}
-			if (client_entry && client_entry->server)
+			if (client_entry && *client_entry->server)
 				purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
 
 
@@ -1367,10 +1403,23 @@
 		break;
 
 	case SILC_COMMAND_DETACH:
-		if (!success) {
-			purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
-					  silc_get_status_message(status));
-			return;
+		{
+			const char *file;
+			SilcBuffer detach_data;
+
+			if (status != SILC_STATUS_OK) {
+			  purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
+					      silc_get_status_message(error));
+			  return;
+			}
+
+			detach_data = va_arg(ap, SilcBuffer);
+
+			/* Save the detachment data to file. */
+			file = silcpurple_session_file(purple_account_get_username(sg->account));
+			g_unlink(file);
+			silc_file_writefile(file, (const char *)silc_buffer_data(detach_data),
+					    silc_buffer_len(detach_data));
 		}
 		break;
 
@@ -1378,19 +1427,19 @@
 		{
 			SilcChannelEntry channel;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
-						  silc_get_status_message(status));
+						    silc_get_status_message(error));
 				return;
 			}
 
-			channel = va_arg(vp, SilcChannelEntry);
+			channel = va_arg(ap, SilcChannelEntry);
 
 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-									channel->channel_name, sg->account);
+								      channel->channel_name, sg->account);
 			if (!convo) {
 				purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
-								 channel->channel_name);
+						   channel->channel_name);
 				break;
 			}
 
@@ -1402,39 +1451,37 @@
 
 	case SILC_COMMAND_NICK:
 		{
-			/* I don't think we should need to do this because the server should
-			 * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own
-			 * nick, but it isn't, so we deal with it here instead. Stu. */
 			SilcClientEntry local_entry;
 			SilcHashTableList htl;
 			SilcChannelUser chu;
-			const char *oldnick;
+			const char *oldnick, *newnick;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
-						silc_get_status_message(status));
+						    silc_get_status_message(error));
 				return;
 			}
 
-			local_entry = va_arg(vp, SilcClientEntry);
+			local_entry = va_arg(ap, SilcClientEntry);
+			newnick = va_arg(ap, char *);
 
 			/* Change nick on all channels */
 			silc_hash_table_list(local_entry->channels, &htl);
 			while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
 				convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-									chu->channel->channel_name, sg->account);
+									      chu->channel->channel_name, sg->account);
 				if (!convo)
 					continue;
 				oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
-				if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) {
+				if (strcmp(oldnick, purple_normalize(purple_conversation_get_account(convo), newnick))) {
 					purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
-							oldnick, local_entry->nickname);
-					purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname);
+								     oldnick, newnick);
+					purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), newnick);
 				}
 			}
 			silc_hash_table_list_reset(&htl);
 
-			purple_connection_set_display_name(gc, local_entry->nickname);
+			purple_connection_set_display_name(gc, newnick);
 		}
 		break;
 
@@ -1447,34 +1494,34 @@
 			if (sg->roomlist_canceled)
 				break;
 
-			if (!success) {
+			if (error != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
-						  silc_get_status_message(status));
+						    silc_get_status_message(error));
 				purple_roomlist_set_in_progress(sg->roomlist, FALSE);
 				purple_roomlist_unref(sg->roomlist);
 				sg->roomlist = NULL;
 				return;
 			}
 
-			(void)va_arg(vp, SilcChannelEntry);
-			name = va_arg(vp, char *);
+			(void)va_arg(ap, SilcChannelEntry);
+			name = va_arg(ap, char *);
 			if (!name) {
 				purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
-						  silc_get_status_message(status));
+						    _("Network is empty"));
 				purple_roomlist_set_in_progress(sg->roomlist, FALSE);
 				purple_roomlist_unref(sg->roomlist);
 				sg->roomlist = NULL;
 				return;
 			}
-			topic = va_arg(vp, char *);
-			usercount = va_arg(vp, int);
+			topic = va_arg(ap, char *);
+			usercount = va_arg(ap, int);
 
 			room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
 			purple_roomlist_room_add_field(sg->roomlist, room, name);
 			purple_roomlist_room_add_field(sg->roomlist, room,
-						     SILC_32_TO_PTR(usercount));
+						       SILC_32_TO_PTR(usercount));
 			purple_roomlist_room_add_field(sg->roomlist, room,
-						     topic ? topic : "");
+						       topic ? topic : "");
 			purple_roomlist_room_add(sg->roomlist, room);
 
 			if (status == SILC_STATUS_LIST_END ||
@@ -1490,21 +1537,21 @@
 		{
 			SilcPublicKey public_key;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Get Public Key"),
-						  _("Cannot fetch the public key"),
-						  silc_get_status_message(status));
+						    _("Cannot fetch the public key"),
+						    silc_get_status_message(error));
 				return;
 			}
 
-			(void)va_arg(vp, SilcUInt32);
-			(void)va_arg(vp, void *);
-			public_key = va_arg(vp, SilcPublicKey);
+			(void)va_arg(ap, SilcUInt32);
+			(void)va_arg(ap, void *);
+			public_key = va_arg(ap, SilcPublicKey);
 
 			if (!public_key)
 				purple_notify_error(gc, _("Get Public Key"),
-						  _("Cannot fetch the public key"),
-						  _("No public key was received"));
+						    _("Cannot fetch the public key"),
+						    _("No public key was received"));
 		}
 		break;
 
@@ -1515,16 +1562,16 @@
 			char *server_info;
 			char tmp[256];
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Server Information"),
-						  _("Cannot get server information"),
-						  silc_get_status_message(status));
+						    _("Cannot get server information"),
+						    silc_get_status_message(error));
 				return;
 			}
 
-			(void)va_arg(vp, SilcServerEntry);
-			server_name = va_arg(vp, char *);
-			server_info = va_arg(vp, char *);
+			(void)va_arg(ap, SilcServerEntry);
+			server_name = va_arg(ap, char *);
+			server_info = va_arg(ap, char *);
 
 			if (server_name && server_info) {
 				g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
@@ -1536,94 +1583,73 @@
 
 	case SILC_COMMAND_STATS:
 		{
-			SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
-			my_router_ops, cell_clients, cell_channels, cell_servers,
-			clients, channels, servers, routers, server_ops, router_ops;
-			SilcUInt32 buffer_length;
-			SilcBufferStruct buf;
-
-			unsigned char *server_stats;
+			SilcClientStats *stats;
 			char *msg;
 
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Server Statistics"),
-						_("Cannot get server statistics"),
-						silc_get_status_message(status));
+						    _("Cannot get server statistics"),
+						    silc_get_status_message(error));
 				return;
 			}
 
-			server_stats = va_arg(vp, unsigned char *);
-			buffer_length = va_arg(vp, SilcUInt32);
-			if (!server_stats || !buffer_length) {
-				purple_notify_error(gc, _("Server Statistics"),
-						_("No server statistics available"), NULL);
-				break;
-			}
-			silc_buffer_set(&buf, server_stats, buffer_length);
-			silc_buffer_unformat(&buf,
-					SILC_STR_UI_INT(&starttime),
-					SILC_STR_UI_INT(&uptime),
-					SILC_STR_UI_INT(&my_clients),
-					SILC_STR_UI_INT(&my_channels),
-					SILC_STR_UI_INT(&my_server_ops),
-					SILC_STR_UI_INT(&my_router_ops),
-					SILC_STR_UI_INT(&cell_clients),
-					SILC_STR_UI_INT(&cell_channels),
-					SILC_STR_UI_INT(&cell_servers),
-					SILC_STR_UI_INT(&clients),
-					SILC_STR_UI_INT(&channels),
-					SILC_STR_UI_INT(&servers),
-					SILC_STR_UI_INT(&routers),
-					SILC_STR_UI_INT(&server_ops),
-					SILC_STR_UI_INT(&router_ops),
-					SILC_STR_END);
+			stats = va_arg(ap, SilcClientStats *);
 
 			msg = g_strdup_printf(_("Local server start time: %s\n"
-					"Local server uptime: %s\n"
-					"Local server clients: %d\n"
-					"Local server channels: %d\n"
-					"Local server operators: %d\n"
-					"Local router operators: %d\n"
-					"Local cell clients: %d\n"
-					"Local cell channels: %d\n"
-					"Local cell servers: %d\n"
-					"Total clients: %d\n"
-					"Total channels: %d\n"
-					"Total servers: %d\n"
-					"Total routers: %d\n"
-					"Total server operators: %d\n"
-					"Total router operators: %d\n"),
-					silc_get_time(starttime),
-					purple_str_seconds_to_string((int)uptime),
-					(int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops,
-					(int)cell_clients, (int)cell_channels, (int)cell_servers,
-					(int)clients, (int)channels, (int)servers, (int)routers,
-					(int)server_ops, (int)router_ops);
+						"Local server uptime: %s\n"
+						"Local server clients: %d\n"
+						"Local server channels: %d\n"
+						"Local server operators: %d\n"
+						"Local router operators: %d\n"
+						"Local cell clients: %d\n"
+						"Local cell channels: %d\n"
+						"Local cell servers: %d\n"
+						"Total clients: %d\n"
+						"Total channels: %d\n"
+						"Total servers: %d\n"
+						"Total routers: %d\n"
+						"Total server operators: %d\n"
+						"Total router operators: %d\n"),
+					      silc_time_string(stats->starttime),
+					      purple_str_seconds_to_string((int)stats->uptime),
+					      (int)stats->my_clients,
+					      (int)stats->my_channels,
+					      (int)stats->my_server_ops,
+					      (int)stats->my_router_ops,
+					      (int)stats->cell_clients,
+					      (int)stats->cell_channels,
+					      (int)stats->cell_servers,
+					      (int)stats->clients,
+					      (int)stats->channels,
+					      (int)stats->servers,
+					      (int)stats->routers,
+					      (int)stats->server_ops,
+					      (int)stats->router_ops);
 
 			purple_notify_info(gc, NULL,
-					_("Network Statistics"), msg);
+					   _("Network Statistics"), msg);
 			g_free(msg);
 		}
 		break;
 
 	case SILC_COMMAND_PING:
 		{
-			if (!success) {
+			if (status != SILC_STATUS_OK) {
 				purple_notify_error(gc, _("Ping"), _("Ping failed"),
-								  silc_get_status_message(status));
+						    silc_get_status_message(error));
 				return;
 			}
 
 			purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
-							 NULL);
+					   NULL);
 		}
 		break;
 
 	case SILC_COMMAND_KILL:
-		if (!success) {
+		if (status != SILC_STATUS_OK) {
 			purple_notify_error(gc, _("Kill User"),
-					  _("Could not kill user"),
-					  silc_get_status_message(status));
+					    _("Could not kill user"),
+					    silc_get_status_message(error));
 			return;
 		}
 		break;
@@ -1631,188 +1657,108 @@
 	case SILC_COMMAND_CMODE:
 		{
 			SilcChannelEntry channel_entry;
-			SilcBuffer channel_pubkeys;
+			SilcDList channel_pubkeys, list;
+			SilcArgumentDecodedList e;
 
-			if (!success)
+			if (status != SILC_STATUS_OK)
 				return;
 
-			channel_entry = va_arg(vp, SilcChannelEntry);
-			(void)va_arg(vp, SilcUInt32);
-			(void)va_arg(vp, SilcPublicKey);
-			channel_pubkeys = va_arg(vp, SilcBuffer);
+			channel_entry = va_arg(ap, SilcChannelEntry);
+			(void)va_arg(ap, SilcUInt32);
+			(void)va_arg(ap, SilcPublicKey);
+			channel_pubkeys = va_arg(ap, SilcDList);
+
+			if (!sg->chpk)
+				break;
+
+			list = silc_dlist_init();
 
-			if (sg->chpk)
-				silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys);
+			if (channel_pubkeys) {
+			  silc_dlist_start(channel_pubkeys);
+			  while ((e = silc_dlist_get(channel_pubkeys))) {
+				if (e->arg_type == 0x00 ||
+				    e->arg_type == 0x03)
+				  silc_dlist_add(list, silc_pkcs_public_key_copy(e->argument));
+			  }
+			}
+			silcpurple_chat_chauth_show(sg, channel_entry, list);
+		}
+		break;
+
+	case SILC_COMMAND_WATCH:
+		if (status != SILC_STATUS_OK) {
+			purple_notify_error(gc, _("WATCH"), _("Cannot watch user"),
+					    silc_get_status_message(error));
+			return;
 		}
 		break;
 
 	default:
-		if (success)
+		if (status == SILC_STATUS_OK)
 			purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
 		else
 			purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
-							silc_get_status_message(status));
+					  silc_get_status_message(error));
 		break;
 	}
-
-	va_end(vp);
 }
 
-
-/* Called to indicate that connection was either successfully established
-   or connecting failed.  This is also the first time application receives
-   the SilcClientConnection object which it should save somewhere.
-   If the `success' is FALSE the application must always call the function
-   silc_client_close_connection. */
-
-static void
-silc_connected(SilcClient client, SilcClientConnection conn,
-	       SilcClientConnectionStatus status)
-{
-	PurpleConnection *gc = client->application;
-	SilcPurple sg;
-	gboolean reject_watch, block_invites, block_ims;
-
-	if (gc == NULL) {
-		silc_client_close_connection(client, conn);
-		return;
-	}
-	sg = gc->proto_data;
-
-	switch (status) {
-	case SILC_CLIENT_CONN_SUCCESS:
-	case SILC_CLIENT_CONN_SUCCESS_RESUME:
-		purple_connection_set_state(gc, PURPLE_CONNECTED);
-
-		/* Send the server our buddy list */
-		silcpurple_send_buddylist(gc);
-
-		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-
-		/* Send any UMODEs configured for account */
-		reject_watch = purple_account_get_bool(sg->account, "reject-watch", FALSE);
-		block_invites = purple_account_get_bool(sg->account, "block-invites", FALSE);
-		block_ims = purple_account_get_bool(sg->account, "block-ims", FALSE);
-		if (reject_watch || block_invites || block_ims) {
-			char m[5];
-			g_snprintf(m, sizeof(m), "+%s%s%s",
-					   reject_watch ? "w" : "",
-					   block_invites ? "I" : "",
-					   block_ims ? "P" : "");
-			silc_client_command_call(sg->client, sg->conn, NULL,
-					"UMODE", m, NULL);
-		}
+/* Generic command reply callback for silc_client_command_send.  Simply
+   calls the default command_reply client operation callback */
 
-		return;
-		break;
-	case SILC_CLIENT_CONN_ERROR:
-		purple_connection_error(gc, _("Error during connecting to SILC Server"));
-		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-		break;
-
-	case SILC_CLIENT_CONN_ERROR_KE:
-		purple_connection_error(gc, _("Key Exchange failed"));
-		break;
-
-	case SILC_CLIENT_CONN_ERROR_AUTH:
-		purple_connection_error(gc, _("Authentication failed"));
-		break;
-
-	case SILC_CLIENT_CONN_ERROR_RESUME:
-		purple_connection_error(gc,
-				      _("Resuming detached session failed. "
-					"Press Reconnect to create new connection."));
-		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-		break;
-
-	case SILC_CLIENT_CONN_ERROR_TIMEOUT:
-		purple_connection_error(gc, _("Connection Timeout"));
-		break;
-	}
-
-	/* Error */
-	sg->conn = NULL;
-	silc_client_close_connection(client, conn);
-}
-
-
-/* Called to indicate that connection was disconnected to the server.
-   The `status' may tell the reason of the disconnection, and if the
-   `message' is non-NULL it may include the disconnection message
-   received from server. */
-
-static void
-silc_disconnected(SilcClient client, SilcClientConnection conn,
-		  SilcStatus status, const char *message)
+SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn,
+				  SilcCommand command, SilcStatus status,
+				  SilcStatus error, void *context, va_list ap)
 {
-	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
-
-	if (sg->resuming && !sg->detaching)
-		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
-
-	sg->conn = NULL;
-
-	/* Close the connection */
-	if (!sg->detaching)
-		purple_connection_error(gc, _("Disconnected by server"));
-	else
-		/* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
-		purple_account_disconnect(purple_connection_get_account(gc));
+  silc_command_reply(client, conn, command, status, error, ap);
+  return TRUE;
 }
 
 
 typedef struct {
-	SilcGetAuthMeth completion;
+        union {
+	  SilcAskPassphrase ask_pass;
+	  SilcGetAuthMeth get_auth;
+	} u;
 	void *context;
-} *SilcPurpleGetAuthMethod;
-
-/* Callback called when we've received the authentication method information
-   from the server after we've requested it. */
-
-static void silc_get_auth_method_callback(SilcClient client,
-					  SilcClientConnection conn,
-					  SilcAuthMethod auth_meth,
-					  void *context)
-{
-	SilcPurpleGetAuthMethod internal = context;
+} *SilcPurpleAskPassphrase;
 
-	switch (auth_meth) {
-	case SILC_AUTH_NONE:
-		/* No authentication required. */
-		(*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
-		break;
+static void
+silc_ask_auth_password_cb(const unsigned char *passphrase,
+			  SilcUInt32 passphrase_len, void *context)
+{
+	SilcPurpleAskPassphrase internal = context;
 
-	case SILC_AUTH_PASSWORD:
-		/* By returning NULL here the library will ask the passphrase from us
-		   by calling the silc_ask_passphrase. */
-		(*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
-		break;
-
-	case SILC_AUTH_PUBLIC_KEY:
-		/* Do not get the authentication data now, the library will generate
-		   it using our default key, if we do not provide it here. */
-		(*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
-		break;
-	}
-
+	if (!passphrase || !(*passphrase))
+	  internal->u.get_auth(SILC_AUTH_NONE, NULL, 0, internal->context);
+	else
+	  internal->u.get_auth(SILC_AUTH_PASSWORD,
+			       (unsigned char *)passphrase,
+			       passphrase_len, internal->context);
 	silc_free(internal);
 }
 
 /* Find authentication method and authentication data by hostname and
-   port. The hostname may be IP address as well. When the authentication
-   method has been resolved the `completion' callback with the found
-   authentication method and authentication data is called. The `conn'
-   may be NULL. */
+   port. The hostname may be IP address as well. The `auth_method' is
+   the authentication method the remote connection requires.  It is
+   however possible that remote accepts also some other authentication
+   method.  Application should use the method that may have been
+   configured for this connection.  If none has been configured it should
+   use the required `auth_method'.  If the `auth_method' is
+   SILC_AUTH_NONE, server does not require any authentication or the
+   required authentication method is not known.  The `completion'
+   callback must be called to deliver the chosen authentication method
+   and data. The `conn' may be NULL. */
 
 static void
 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
 		     char *hostname, SilcUInt16 port,
+		     SilcAuthMethod auth_method,
 		     SilcGetAuthMeth completion, void *context)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
-	SilcPurpleGetAuthMethod internal;
+	SilcPurpleAskPassphrase internal;
 	const char *password;
 
 	/* Progress */
@@ -1821,72 +1767,71 @@
 	else
 		purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
 
-	/* Check configuration if we have this connection configured.  If we
-	   have then return that data immediately, as it's faster way. */
-	if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
-		completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
+	/* Check configuration if we have this connection configured. */
+	if (auth_method == SILC_AUTH_PUBLIC_KEY &&
+	    purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
+		completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
 		return;
 	}
-	password = purple_connection_get_password(gc);
-	if (password && *password) {
-		completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
+	if (auth_method == SILC_AUTH_PASSWORD) {
+		password = purple_connection_get_password(gc);
+		if (password && *password) {
+		  completion(SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
+		  return;
+		}
+
+		/* Ask password from user */
+		internal = silc_calloc(1, sizeof(*internal));
+		if (!internal)
+		  return;
+		internal->u.get_auth = completion;
+		internal->context = context;
+		silc_ask_passphrase(client, conn, silc_ask_auth_password_cb,
+				    internal);
 		return;
 	}
 
-	/* Resolve the authentication method from server, as we may not know it. */
-	internal = silc_calloc(1, sizeof(*internal));
-	if (!internal)
-		return;
-	internal->completion = completion;
-	internal->context = context;
-	silc_client_request_authentication_method(client, conn,
-						  silc_get_auth_method_callback,
-						  internal);
+	completion(SILC_AUTH_NONE, NULL, 0, context);
 }
 
 
-/* Verifies received public key. The `conn_type' indicates which entity
-   (server, client etc.) has sent the public key. If user decides to trust
-   the application may save the key as trusted public key for later
-   use. The `completion' must be called after the public key has been
-   verified. */
+/* Called to verify received public key. The `conn_type' indicates which
+   entity (server or client) has sent the public key. If user decides to
+   trust the key the application may save the key as trusted public key for
+   later use. The `completion' must be called after the public key has
+   been verified. */
 
 static void
 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
-		       SilcSocketType conn_type, unsigned char *pk,
-		       SilcUInt32 pk_len, SilcSKEPKType pk_type,
+		       SilcConnectionType conn_type,
+		       SilcPublicKey public_key,
 		       SilcVerifyPublicKey completion, void *context)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
 
-	if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
-			  conn_type == SILC_SOCKET_TYPE_ROUTER)) {
+	if (!sg->conn && (conn_type == SILC_CONN_SERVER ||
+			  conn_type == SILC_CONN_ROUTER)) {
 		/* Progress */
 		if (sg->resuming)
 			purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
 		else
 			purple_connection_update_progress(gc, _("Verifying server public key"),
-							3, 5);
+							  3, 5);
 	}
 
 	/* Verify public key */
-	silcpurple_verify_public_key(client, conn, NULL, conn_type, pk,
-				   pk_len, pk_type, completion, context);
+	silcpurple_verify_public_key(client, conn, NULL, conn_type,
+				     public_key, completion, context);
 }
 
-typedef struct {
-	SilcAskPassphrase completion;
-	void *context;
-} *SilcPurpleAskPassphrase;
-
 static void
 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
 {
 	if (!passphrase || !(*passphrase))
-		internal->completion(NULL, 0, internal->context);
+		internal->u.ask_pass(NULL, 0, internal->context);
 	else
-		internal->completion((unsigned char *)passphrase,
+		internal->u.ask_pass((unsigned char *)passphrase,
 				     strlen(passphrase), internal->context);
 	silc_free(internal);
 }
@@ -1905,97 +1850,32 @@
 
 	if (!internal)
 		return;
-	internal->completion = completion;
+	internal->u.ask_pass = completion;
 	internal->context = context;
 	purple_request_input(gc, _("Passphrase"), NULL,
-			   _("Passphrase required"), NULL, FALSE, TRUE, NULL,
-			   _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
-			   _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
-			   purple_connection_get_account(gc), NULL, NULL, internal);
+			     _("Passphrase required"), NULL, FALSE, TRUE, NULL,
+			     _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
+			     _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
+			     purple_connection_get_account(gc), NULL, NULL, internal);
 }
 
 
-/* Notifies application that failure packet was received.  This is called
-   if there is some protocol active in the client.  The `protocol' is the
-   protocol context.  The `failure' is opaque pointer to the failure
-   indication.  Note, that the `failure' is protocol dependant and
-   application must explicitly cast it to correct type.  Usually `failure'
-   is 32 bit failure type (see protocol specs for all protocol failure
-   types). */
+/* Called to indicate that incoming key agreement request has been
+   received.  If the application wants to perform key agreement it may
+   call silc_client_perform_key_agreement to initiate key agreement or
+   silc_client_send_key_agreement to provide connection point to the
+   remote client in case the `hostname' is NULL.  If key agreement is
+   not desired this request can be ignored.  The `protocol' is either
+   value 0 for TCP or value 1 for UDP. */
 
 static void
-silc_failure(SilcClient client, SilcClientConnection conn,
-	     SilcProtocol protocol, void *failure)
+silc_key_agreement(SilcClient client, SilcClientConnection conn,
+		   SilcClientEntry client_entry,
+		   const char *hostname, SilcUInt16 protocol,
+		   SilcUInt16 port)
 {
-	PurpleConnection *gc = client->application;
-	char buf[128];
-
-	memset(buf, 0, sizeof(buf));
-
-	if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
-		SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
-
-		if (status == SILC_SKE_STATUS_BAD_VERSION)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Version mismatch, upgrade your client"));
-		if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Remote does not trust/support your public key"));
-		if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Remote does not support proposed KE group"));
-		if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Remote does not support proposed cipher"));
-		if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Remote does not support proposed PKCS"));
-		if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Remote does not support proposed hash function"));
-		if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
-			g_snprintf(buf, sizeof(buf),
-				   _("Failure: Remote does not support proposed HMAC"));
-		if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
-			g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
-		if (status == SILC_SKE_STATUS_INVALID_COOKIE)
-			g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
-
-		/* Show the error on the progress bar.  A more generic error message
-		   is going to be showed to user after this in the silc_connected. */
-		purple_connection_update_progress(gc, buf, 2, 5);
-	}
-
-	if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
-		SilcUInt32 err = SILC_PTR_TO_32(failure);
-
-		if (err == SILC_AUTH_FAILED)
-			g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
-
-		/* Show the error on the progress bar.  A more generic error message
-		   is going to be showed to user after this in the silc_connected. */
-		purple_connection_update_progress(gc, buf, 4, 5);
-	}
-}
-
-/* Asks whether the user would like to perform the key agreement protocol.
-   This is called after we have received an key agreement packet or an
-   reply to our key agreement packet. This returns TRUE if the user wants
-   the library to perform the key agreement protocol and FALSE if it is not
-   desired (application may start it later by calling the function
-   silc_client_perform_key_agreement). If TRUE is returned also the
-   `completion' and `context' arguments must be set by the application. */
-
-static bool
-silc_key_agreement(SilcClient client, SilcClientConnection conn,
-		   SilcClientEntry client_entry, const char *hostname,
-		   SilcUInt16 port, SilcKeyAgreementCallback *completion,
-		   void **context)
-{
-	silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port);
-	*completion = NULL;
-	*context = NULL;
-	return FALSE;
+	silcpurple_buddy_keyagr_request(client, conn, client_entry,
+					hostname, port, protocol);
 }
 
 
@@ -2012,39 +1892,7 @@
 	 const char *hostname, SilcUInt16 port)
 {
 	silcpurple_ftp_request(client, conn, client_entry, session_id,
-			     hostname, port);
-}
-
-
-/* Delivers SILC session detachment data indicated by `detach_data' to the
-   application.  If application has issued SILC_COMMAND_DETACH command
-   the client session in the SILC network is not quit.  The client remains
-   in the network but is detached.  The detachment data may be used later
-   to resume the session in the SILC Network.  The appliation is
-   responsible of saving the `detach_data', to for example in a file.
-
-   The detachment data can be given as argument to the functions
-   silc_client_connect_to_server, or silc_client_add_connection when
-   creating connection to remote server, inside SilcClientConnectionParams
-   structure.  If it is provided the client library will attempt to resume
-   the session in the network.  After the connection is created
-   successfully, the application is responsible of setting the user
-   interface for user into the same state it was before detaching (showing
-   same channels, channel modes, etc).  It can do this by fetching the
-   information (like joined channels) from the client library. */
-
-static void
-silc_detach(SilcClient client, SilcClientConnection conn,
-	    const unsigned char *detach_data, SilcUInt32 detach_data_len)
-{
-	PurpleConnection *gc = client->application;
-	SilcPurple sg = gc->proto_data;
-	const char *file;
-
-	/* Save the detachment data to file. */
-	file = silcpurple_session_file(purple_account_get_username(sg->account));
-	g_unlink(file);
-	silc_file_writefile(file, (char *)detach_data, detach_data_len);
+			       hostname, port);
 }
 
 SilcClientOperations ops = {
@@ -2054,13 +1902,9 @@
 	silc_notify,
 	silc_command,
 	silc_command_reply,
-	silc_connected,
-	silc_disconnected,
 	silc_get_auth_method,
 	silc_verify_public_key,
 	silc_ask_passphrase,
-	silc_failure,
 	silc_key_agreement,
-	silc_ftp,
-	silc_detach
+	silc_ftp
 };
--- a/libpurple/protocols/silc/pk.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/pk.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 
@@ -31,18 +31,16 @@
 	char *entity_name;
 	char *fingerprint;
 	char *babbleprint;
-	unsigned char *pk;
-	SilcUInt32 pk_len;
-	SilcSKEPKType pk_type;
+	SilcPublicKey public_key;
 	SilcVerifyPublicKey completion;
 	void *context;
 	gboolean changed;
 } *PublicKeyVerify;
 
 static void silcpurple_verify_ask(const char *entity,
-				const char *fingerprint,
-				const char *babbleprint,
-				PublicKeyVerify verify);
+				  const char *fingerprint,
+				  const char *babbleprint,
+				  PublicKeyVerify verify);
 
 static void silcpurple_verify_cb(PublicKeyVerify verify, gint id)
 {
@@ -54,8 +52,8 @@
 			verify->completion(TRUE, verify->context);
 
 		/* Save the key for future checking */
-		silc_pkcs_save_public_key_data(verify->filename, verify->pk,
-					       verify->pk_len, SILC_PKCS_FILE_PEM);
+		silc_pkcs_save_public_key(verify->filename, verify->public_key,
+					  SILC_PKCS_FILE_BASE64);
 	}
 
 	silc_free(verify->filename);
@@ -63,7 +61,7 @@
 	silc_free(verify->entity_name);
 	silc_free(verify->fingerprint);
 	silc_free(verify->babbleprint);
-	silc_free(verify->pk);
+	silc_pkcs_public_key_free(verify->public_key);
 	silc_free(verify);
 }
 
@@ -74,27 +72,23 @@
 	   should have option for the dialogs whether the buttons close them
 	   or not. */
 	silcpurple_verify_ask(verify->entity, verify->fingerprint,
-			    verify->babbleprint, verify);
+			      verify->babbleprint, verify);
 }
 
 static void silcpurple_verify_details(PublicKeyVerify verify, gint id)
 {
-	SilcPublicKey public_key;
 	PurpleConnection *gc = verify->client->application;
 	SilcPurple sg = gc->proto_data;
 
-	silc_pkcs_public_key_decode(verify->pk, verify->pk_len,
-				    &public_key);
-	silcpurple_show_public_key(sg, verify->entity_name, public_key,
-				 G_CALLBACK(silcpurple_verify_details_cb),
-				 verify);
-	silc_pkcs_public_key_free(public_key);
+	silcpurple_show_public_key(sg, verify->entity_name, verify->public_key,
+				   G_CALLBACK(silcpurple_verify_details_cb),
+				   verify);
 }
 
 static void silcpurple_verify_ask(const char *entity,
-				const char *fingerprint,
-				const char *babbleprint,
-				PublicKeyVerify verify)
+				  const char *fingerprint,
+				  const char *babbleprint,
+				  PublicKeyVerify verify)
 {
 	PurpleConnection *gc = verify->client->application;
 	char tmp[256], tmp2[256];
@@ -114,18 +108,17 @@
 		     "%s\n%s\n"), entity, fingerprint, babbleprint);
 
 	purple_request_action(gc, _("Verify Public Key"), tmp, tmp2,
-						PURPLE_DEFAULT_ACTION_NONE,
-						purple_connection_get_account(gc), entity, NULL, verify, 3,
-			    _("Yes"), G_CALLBACK(silcpurple_verify_cb),
-			    _("No"), G_CALLBACK(silcpurple_verify_cb),
-			    _("_View..."), G_CALLBACK(silcpurple_verify_details));
+			      PURPLE_DEFAULT_ACTION_NONE,
+			      purple_connection_get_account(gc), entity, NULL, verify, 3,
+			      _("Yes"), G_CALLBACK(silcpurple_verify_cb),
+			      _("No"), G_CALLBACK(silcpurple_verify_cb),
+			      _("_View..."), G_CALLBACK(silcpurple_verify_details));
 }
 
 void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
-				const char *name, SilcSocketType conn_type,
-				unsigned char *pk, SilcUInt32 pk_len,
-				SilcSKEPKType pk_type,
-				SilcVerifyPublicKey completion, void *context)
+				  const char *name, SilcConnectionType conn_type,
+				  SilcPublicKey public_key,
+				  SilcVerifyPublicKey completion, void *context)
 {
 	PurpleConnection *gc = client->application;
 	int i;
@@ -133,14 +126,18 @@
 	char *fingerprint, *babbleprint;
 	struct passwd *pw;
 	struct stat st;
-	char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
-			 conn_type == SILC_SOCKET_TYPE_ROUTER) ?
+	char *entity = ((conn_type == SILC_CONN_SERVER ||
+			 conn_type == SILC_CONN_ROUTER) ?
 			"server" : "client");
 	PublicKeyVerify verify;
+	const char *ip, *hostname;
+	SilcUInt16 port;
+	unsigned char *pk;
+	SilcUInt32 pk_len;
 
-	if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+	if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
 		purple_notify_error(gc, _("Verify Public Key"),
-				  _("Unsupported public key type"), NULL);
+				    _("Unsupported public key type"), NULL);
 		if (completion)
 			completion(FALSE, context);
 		return;
@@ -157,17 +154,22 @@
 	memset(filename2, 0, sizeof(filename2));
 	memset(file, 0, sizeof(file));
 
-	if (conn_type == SILC_SOCKET_TYPE_SERVER ||
-	    conn_type == SILC_SOCKET_TYPE_ROUTER) {
+	silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream),
+				    NULL, &hostname, &ip, &port);
+
+	pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+
+	if (conn_type == SILC_CONN_SERVER ||
+	    conn_type == SILC_CONN_ROUTER) {
 		if (!name) {
 			g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
-				   conn->sock->ip, conn->sock->port);
+				   ip, port);
 			g_snprintf(filename, sizeof(filename) - 1,
 				   "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
 				   silcpurple_silcdir(), entity, file);
 
 			g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
-				   conn->sock->hostname, conn->sock->port);
+				   hostname, port);
 			g_snprintf(filename2, sizeof(filename2) - 1,
 				   "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
 				   silcpurple_silcdir(), entity, file);
@@ -176,7 +178,7 @@
 			hostf = filename2;
 		} else {
 			g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity,
-				   name, conn->sock->port);
+				   name, port);
 			g_snprintf(filename, sizeof(filename) - 1,
 				   "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s",
 				   silcpurple_silcdir(), entity, file);
@@ -206,12 +208,10 @@
 	verify->conn = conn;
 	verify->filename = strdup(ipf);
 	verify->entity = strdup(entity);
-	verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
-			       (name ? strdup(name) : strdup(conn->sock->hostname))
+	verify->entity_name = (conn_type != SILC_CONN_CLIENT ?
+			       (name ? strdup(name) : strdup(hostname))
 			       : NULL);
-	verify->pk = silc_memdup(pk, pk_len);
-	verify->pk_len = pk_len;
-	verify->pk_type = pk_type;
+	verify->public_key = silc_pkcs_public_key_copy(public_key);
 	verify->completion = completion;
 	verify->context = context;
 	fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
@@ -221,7 +221,7 @@
 	if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) {
 		/* Key does not exist, ask user to verify the key and save it */
 		silcpurple_verify_ask(name ? name : entity,
-				    fingerprint, babbleprint, verify);
+				      fingerprint, babbleprint, verify);
 		return;
 	} else {
 		/* The key already exists, verify it. */
@@ -230,14 +230,8 @@
 		SilcUInt32 encpk_len;
 
 		/* Load the key file, try for both IP filename and hostname filename */
-		if (!silc_pkcs_load_public_key(ipf, &public_key,
-					       SILC_PKCS_FILE_PEM) &&
-		    !silc_pkcs_load_public_key(ipf, &public_key,
-					       SILC_PKCS_FILE_BIN) &&
-		    (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key,
-							   SILC_PKCS_FILE_PEM) &&
-				!silc_pkcs_load_public_key(hostf, &public_key,
-							   SILC_PKCS_FILE_BIN)))) {
+		if (!silc_pkcs_load_public_key(ipf, &public_key) &&
+		    (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key)))) {
 			silcpurple_verify_ask(name ? name : entity,
 					    fingerprint, babbleprint, verify);
 			return;
@@ -266,9 +260,9 @@
 		silc_free(verify->filename);
 		silc_free(verify->entity);
 		silc_free(verify->entity_name);
-		silc_free(verify->pk);
 		silc_free(verify->fingerprint);
 		silc_free(verify->babbleprint);
+		silc_pkcs_public_key_free(verify->public_key);
 		silc_free(verify);
 	}
 }
--- a/libpurple/protocols/silc/silc.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/silc.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 - 2005 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 #include "version.h"
@@ -26,6 +26,15 @@
 extern SilcClientOperations ops;
 static PurplePlugin *silc_plugin = NULL;
 
+/* Error log message callback */
+
+static SilcBool silcpurple_log_error(SilcLogType type, char *message,
+				     void *context)
+{
+	silc_say(NULL, NULL, SILC_CLIENT_MESSAGE_ERROR, message);
+	return TRUE;
+}
+
 static const char *
 silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
 {
@@ -102,8 +111,8 @@
 	idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
 	SILC_PUT32_MSB(mode, mb);
 	silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
-				 ++sg->conn->cmd_ident, 2,
-				 1, idp->data, idp->len,
+				 silcpurple_command_reply, NULL, 2,
+				 1, idp->data, silc_buffer_len(idp),
 				 2, mb, sizeof(mb));
 	silc_buffer_free(idp);
 }
@@ -115,67 +124,169 @@
 silcpurple_keepalive(PurpleConnection *gc)
 {
 	SilcPurple sg = gc->proto_data;
-	silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT,
-				NULL, 0);
+	silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0,
+			 NULL, 0);
 }
 
 static gboolean
 silcpurple_scheduler(gpointer *context)
 {
-	SilcPurple sg = (SilcPurple)context;
-	silc_client_run_one(sg->client);
+	SilcClient client = (SilcClient)context;
+	silc_client_run_one(client);
 	return TRUE;
 }
 
 static void
-silcpurple_nickname_parse(const char *nickname,
-			char **ret_nickname)
+silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
+		      SilcClientConnectionStatus status, SilcStatus error,
+		      const char *message, void *context)
 {
-	silc_parse_userfqdn(nickname, ret_nickname, NULL);
+	PurpleConnection *gc = context;
+	SilcPurple sg;
+	gboolean reject_watch, block_invites, block_ims;
+
+	sg = gc->proto_data;
+
+	switch (status) {
+	case SILC_CLIENT_CONN_SUCCESS:
+	case SILC_CLIENT_CONN_SUCCESS_RESUME:
+		sg->conn = conn;
+
+		/* Connection created successfully */
+		purple_connection_set_state(gc, PURPLE_CONNECTED);
+
+		/* Send the server our buddy list */
+		silcpurple_send_buddylist(gc);
+
+		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+
+		/* Send any UMODEs configured for account */
+		reject_watch = purple_account_get_bool(sg->account, "reject-watch", FALSE);
+		block_invites = purple_account_get_bool(sg->account, "block-invites", FALSE);
+		block_ims = purple_account_get_bool(sg->account, "block-ims", FALSE);
+		if (reject_watch || block_invites || block_ims) {
+			char m[5];
+			g_snprintf(m, sizeof(m), "+%s%s%s",
+				   reject_watch ? "w" : "",
+				   block_invites ? "I" : "",
+				   block_ims ? "P" : "");
+			silc_client_command_call(sg->client, sg->conn, NULL,
+						 "UMODE", m, NULL);
+		}
+
+		/* Set default attributes */
+		if (!purple_account_get_bool(sg->account, "reject-attrs", FALSE)) {
+		  SilcUInt32 mask;
+		  char tz[16];
+#ifdef SILC_ATTRIBUTE_USER_ICON
+		  PurpleStoredImage *img;
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+		  struct utsname u;
+#endif
+
+		  mask = SILC_ATTRIBUTE_MOOD_NORMAL;
+		  silc_client_attribute_add(client, conn,
+					    SILC_ATTRIBUTE_STATUS_MOOD,
+					    SILC_32_TO_PTR(mask),
+					    sizeof(SilcUInt32));
+		  mask = SILC_ATTRIBUTE_CONTACT_CHAT;
+		  silc_client_attribute_add(client, conn,
+					    SILC_ATTRIBUTE_PREFERRED_CONTACT,
+					    SILC_32_TO_PTR(mask),
+					    sizeof(SilcUInt32));
+#ifdef HAVE_SYS_UTSNAME_H
+		  if (!uname(&u)) {
+			SilcAttributeObjDevice dev;
+			memset(&dev, 0, sizeof(dev));
+			dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
+			dev.version = u.release;
+			dev.model = u.sysname;
+			silc_client_attribute_add(client, conn,
+						  SILC_ATTRIBUTE_DEVICE_INFO,
+						  (void *)&dev, sizeof(dev));
+		  }
+#endif
+		  silc_timezone(tz, sizeof(tz));
+		  silc_client_attribute_add(client, conn,
+					    SILC_ATTRIBUTE_TIMEZONE,
+					    (void *)tz, strlen(tz));
+
+#ifdef SILC_ATTRIBUTE_USER_ICON
+		  /* Set our buddy icon */
+		  img = purple_buddy_icons_find_account_icon(sg->account);
+		  silcpurple_buddy_set_icon(gc, img);
+		  purple_imgstore_unref(img);
+#endif
+		}
+
+		return;
+		break;
+
+	case SILC_CLIENT_CONN_DISCONNECTED:
+		/* Disconnected */
+		if (sg->resuming && !sg->detaching)
+		  g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+
+		/* Close the connection */
+		if (!sg->detaching)
+		  purple_connection_error(gc, _("Disconnected by server"));
+		else
+		  /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
+		  purple_account_disconnect(purple_connection_get_account(gc));
+		break;
+
+	case SILC_CLIENT_CONN_ERROR:
+		purple_connection_error(gc, _("Error during connecting to SILC Server"));
+		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+		break;
+
+	case SILC_CLIENT_CONN_ERROR_KE:
+		purple_connection_error(gc, _("Key Exchange failed"));
+		break;
+
+	case SILC_CLIENT_CONN_ERROR_AUTH:
+		purple_connection_error(gc, _("Authentication failed"));
+		break;
+
+	case SILC_CLIENT_CONN_ERROR_RESUME:
+		purple_connection_error(gc,
+				      _("Resuming detached session failed. "
+					"Press Reconnect to create new connection."));
+		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
+		break;
+
+	case SILC_CLIENT_CONN_ERROR_TIMEOUT:
+		purple_connection_error(gc, _("Connection Timeout"));
+		break;
+	}
+
+	/* Error */
+	sg->conn = NULL;
 }
 
 static void
-silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
+silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream,
+			  void *context)
 {
-	PurpleConnection *gc = data;
+	PurpleConnection *gc = context;
 	SilcPurple sg;
 	SilcClient client;
-	SilcClientConnection conn;
-	PurpleAccount *account;
 	SilcClientConnectionParams params;
 	const char *dfile;
 
-	g_return_if_fail(gc != NULL);
-
 	sg = gc->proto_data;
 
-	if (source < 0) {
+	if (status != SILC_SOCKET_OK) {
 		purple_connection_error(gc, _("Connection failed"));
+		silc_pkcs_public_key_free(sg->public_key);
+		silc_pkcs_private_key_free(sg->private_key);
+		silc_free(sg);
+		gc->proto_data = NULL;
 		return;
 	}
 
 	client = sg->client;
-	account = sg->account;
-
-	/* Get session detachment data, if available */
-	memset(&params, 0, sizeof(params));
-	dfile = silcpurple_session_file(purple_account_get_username(sg->account));
-	params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
-	if (params.detach_data)
-		params.detach_data[params.detach_data_len] = 0;
-
-	/* Add connection to SILC client library */
-	conn = silc_client_add_connection(
-			  sg->client, &params,
-			  (char *)purple_account_get_string(account, "server",
-							  "silc.silcnet.org"),
-			  purple_account_get_int(account, "port", 706), sg);
-	if (!conn) {
-		purple_connection_error(gc, _("Cannot initialize SILC Client connection"));
-		gc->proto_data = NULL;
-		return;
-	}
-	sg->conn = conn;
 
 	/* Progress */
 	if (params.detach_data) {
@@ -185,73 +296,106 @@
 		purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
 	}
 
-	/* Perform SILC Key Exchange.  The "silc_connected" will be called
-	   eventually. */
-	silc_client_start_key_exchange(sg->client, sg->conn, source);
-
-	/* Set default attributes */
-	if (!purple_account_get_bool(account, "reject-attrs", FALSE)) {
-		SilcUInt32 mask;
-		const char *tmp;
-#ifdef SILC_ATTRIBUTE_USER_ICON
-		PurpleStoredImage *img;
-#endif
-#ifdef HAVE_SYS_UTSNAME_H
-		struct utsname u;
-#endif
+	/* Get session detachment data, if available */
+	memset(&params, 0, sizeof(params));
+	dfile = silcpurple_session_file(purple_account_get_username(sg->account));
+	params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
+	if (params.detach_data)
+		params.detach_data[params.detach_data_len] = 0;
+	params.ignore_requested_attributes =
+		purple_account_get_bool(sg->account, "reject-attrs", FALSE);
+	params.pfs = purple_account_get_bool(sg->account, "pfs", FALSE);
 
-		mask = SILC_ATTRIBUTE_MOOD_NORMAL;
-		silc_client_attribute_add(client, conn,
-					  SILC_ATTRIBUTE_STATUS_MOOD,
-					  SILC_32_TO_PTR(mask),
-					  sizeof(SilcUInt32));
-		mask = SILC_ATTRIBUTE_CONTACT_CHAT;
-		silc_client_attribute_add(client, conn,
-					  SILC_ATTRIBUTE_PREFERRED_CONTACT,
-					  SILC_32_TO_PTR(mask),
-					  sizeof(SilcUInt32));
-#ifdef HAVE_SYS_UTSNAME_H
-		if (!uname(&u)) {
-			SilcAttributeObjDevice dev;
-			memset(&dev, 0, sizeof(dev));
-			dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
-			dev.version = u.release;
-			dev.model = u.sysname;
-			silc_client_attribute_add(client, conn,
-						  SILC_ATTRIBUTE_DEVICE_INFO,
-						  (void *)&dev, sizeof(dev));
-		}
-#endif
-#ifdef _WIN32
-		tmp = _tzname[0];
-#else
-		tmp = tzname[0];
-#endif
-		silc_client_attribute_add(client, conn,
-					  SILC_ATTRIBUTE_TIMEZONE,
-					  (void *)tmp, strlen(tmp));
-
-#ifdef SILC_ATTRIBUTE_USER_ICON
-		/* Set our buddy icon */
-		img = purple_buddy_icons_find_account_icon(account);
-		silcpurple_buddy_set_icon(gc, img);
-		purple_imgstore_unref(img);
-#endif
-	}
+	/* Perform SILC Key Exchange. */
+	silc_client_key_exchange(sg->client, &params, sg->public_key,
+				 sg->private_key, stream, SILC_CONN_SERVER,
+				 silcpurple_connect_cb, gc);
 
 	silc_free(params.detach_data);
 }
 
 static void
+silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
+{
+	PurpleConnection *gc = data;
+	SilcPurple sg;
+
+	g_return_if_fail(gc != NULL);
+
+	sg = gc->proto_data;
+
+	if (source < 0) {
+		purple_connection_error(gc, _("Connection failed"));
+		silc_pkcs_public_key_free(sg->public_key);
+		silc_pkcs_private_key_free(sg->private_key);
+		silc_free(sg);
+		gc->proto_data = NULL;
+		return;
+	}
+
+	/* Wrap socket to TCP stream */
+	silc_socket_tcp_stream_create(source, TRUE, FALSE,
+				      sg->client->schedule,
+				      silcpurple_stream_created, gc);
+}
+
+static void silcpurple_running(SilcClient client, void *context)
+{
+	PurpleAccount *account = context;
+	PurpleConnection *gc = account->gc;
+	SilcPurple sg;
+	char pkd[256], prd[256];
+
+	sg = silc_calloc(1, sizeof(*sg));
+	if (!sg)
+		return;
+	memset(sg, 0, sizeof(*sg));
+	sg->client = client;
+	sg->gc = gc;
+	sg->account = account;
+	sg->scheduler = SILC_PTR_TO_32(gc->proto_data);
+	gc->proto_data = sg;
+
+	/* Progress */
+	purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
+
+	/* Load SILC key pair */
+	g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
+	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
+	if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
+				(char *)purple_account_get_string(account, "private-key", prd),
+				(gc->password == NULL) ? "" : gc->password,
+				&sg->public_key, &sg->private_key)) {
+		g_snprintf(pkd, sizeof(pkd), _("Could not load SILC key pair"));
+		purple_connection_error(gc, pkd);
+		gc->proto_data = NULL;
+		silc_free(sg);
+		return;
+	}
+
+	/* Connect to the SILC server */
+	if (purple_proxy_connect(gc, account,
+				 purple_account_get_string(account, "server",
+							   "silc.silcnet.org"),
+				 purple_account_get_int(account, "port", 706),
+				 silcpurple_login_connected, gc) == NULL)
+	{
+		purple_connection_error(gc, _("Unable to create connection"));
+		gc->proto_data = NULL;
+		silc_free(sg);
+		return;
+	}
+}
+
+static void
 silcpurple_login(PurpleAccount *account)
 {
-	SilcPurple sg;
 	SilcClient client;
-	SilcClientParams params;
 	PurpleConnection *gc;
-	char pkd[256], prd[256];
+	SilcClientParams params;
 	const char *cipher, *hmac;
-	char *realname;
+	char *username, *hostname, *realname, **up;
+	guint scheduler;
 	int i;
 
 	gc = account->gc;
@@ -260,10 +404,7 @@
 	gc->proto_data = NULL;
 
 	memset(&params, 0, sizeof(params));
-	strcat(params.nickname_format, "%n@%h%a");
-	params.nickname_parse = silcpurple_nickname_parse;
-	params.ignore_requested_attributes =
-		purple_account_get_bool(account, "reject-attrs", FALSE);
+	strcat(params.nickname_format, "%n#a");
 
 	/* Allocate SILC client */
 	client = silc_client_alloc(&ops, &params, gc, NULL);
@@ -273,32 +414,28 @@
 	}
 
 	/* Get username, real name and local hostname for SILC library */
-	if (purple_account_get_username(account)) {
-		const char *u = purple_account_get_username(account);
-		char **up = g_strsplit(u, "@", 2);
-		client->username = strdup(up[0]);
-		g_strfreev(up);
-	} else {
-		client->username = silc_get_username();
-		purple_account_set_username(account, client->username);
+	if (!purple_account_get_username(account))
+		purple_account_set_username(account, silc_get_username());
+
+	username = (char *)purple_account_get_username(account);
+	up = g_strsplit(username, "@", 2);
+	username = strdup(up[0]);
+	g_strfreev(up);
+
+	if (!purple_account_get_user_info(account)) {
+		purple_account_set_user_info(account, silc_get_real_name());
+		if (!purple_account_get_user_info(account))
+			purple_account_set_user_info(account,
+						     "John T. Noname");
 	}
-	realname = silc_get_real_name();
-	if (purple_account_get_user_info(account)) {
-		client->realname = strdup(purple_account_get_user_info(account));
-		free(realname);
-	} else if ((silc_get_real_name() != NULL) && (*realname != '\0')) {
-		client->realname = realname;
-		purple_account_set_user_info(account, client->realname);
-	} else {
-		free(realname);
-		client->realname = strdup(_("John Noname"));
-	}
-	client->hostname = silc_net_localhost();
+	realname = (char *)purple_account_get_user_info(account);
+	hostname = silc_net_localhost();
 
-	purple_connection_set_display_name(gc, client->username);
+	purple_connection_set_display_name(gc, username);
 
 	/* Register requested cipher and HMAC */
-	cipher = purple_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER);
+	cipher = purple_account_get_string(account, "cipher",
+					   SILC_DEFAULT_CIPHER);
 	for (i = 0; silc_default_ciphers[i].name; i++)
 		if (!strcmp(silc_default_ciphers[i].name, cipher)) {
 			silc_cipher_register(&(silc_default_ciphers[i]));
@@ -312,7 +449,8 @@
 		}
 
 	/* Init SILC client */
-	if (!silc_client_init(client)) {
+	if (!silc_client_init(client, username, hostname, realname,
+			      silcpurple_running, account)) {
 		gc->wants_to_die = TRUE;
 		purple_connection_error(gc, _("Cannot initialize SILC protocol"));
 		return;
@@ -321,54 +459,20 @@
 	/* Check the ~/.silc dir and create it, and new key pair if necessary. */
 	if (!silcpurple_check_silc_dir(gc)) {
 		gc->wants_to_die = TRUE;
-		purple_connection_error(gc, _("Cannot find/access ~/.silc directory"));
-		return;
-	}
-
-	/* Progress */
-	purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
-
-	/* Load SILC key pair */
-	g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
-	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
-	if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
-							(char *)purple_account_get_string(account, "private-key", prd),
-				(gc->password == NULL) ? "" : gc->password, &client->pkcs,
-				&client->public_key, &client->private_key)) {
-		g_snprintf(pkd, sizeof(pkd), _("Could not load SILC key pair: %s"), strerror(errno));
-		purple_connection_error(gc, pkd);
-		return;
-	}
-
-	sg = silc_calloc(1, sizeof(*sg));
-	if (!sg)
-		return;
-	memset(sg, 0, sizeof(*sg));
-	sg->client = client;
-	sg->gc = gc;
-	sg->account = account;
-	gc->proto_data = sg;
-
-	/* Connect to the SILC server */
-	if (purple_proxy_connect(gc, account,
-			       purple_account_get_string(account, "server",
-						       "silc.silcnet.org"),
-			       purple_account_get_int(account, "port", 706),
-			       silcpurple_login_connected, gc) == NULL)
-	{
-		purple_connection_error(gc, _("Unable to create connection"));
+		purple_connection_error(gc, _("Error loading SILC key pair"));
 		return;
 	}
 
 	/* Schedule SILC using Glib's event loop */
-	sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
+	scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client);
+	gc->proto_data = SILC_32_TO_PTR(scheduler);
 }
 
 static int
 silcpurple_close_final(gpointer *context)
 {
 	SilcPurple sg = (SilcPurple)context;
-	silc_client_stop(sg->client);
+	silc_client_stop(sg->client, NULL, NULL);
 	silc_client_free(sg->client);
 #ifdef HAVE_SILCMIME_H
 	if (sg->mimeass)
@@ -387,7 +491,7 @@
 
 	/* Send QUIT */
 	silc_client_command_call(sg->client, sg->conn, NULL,
-				 "QUIT", "Download this: " PURPLE_WEBSITE, NULL);
+				 "QUIT", "Download Pidgin: " PURPLE_WEBSITE, NULL);
 
 	if (sg->conn)
 		silc_client_close_connection(sg->client, sg->conn);
@@ -595,7 +699,7 @@
 	gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
 		cmms = FALSE, cchat = TRUE, cvideo = FALSE;
 	gboolean device = TRUE;
-	char status[1024];
+	char status[1024], tz[16];
 
 	sg = gc->proto_data;
 	if (!sg)
@@ -722,11 +826,9 @@
 					  purple_account_get_string(sg->account, "vcard", ""),
 					  FALSE);
 	purple_request_field_group_add_field(g, f);
-#ifdef _WIN32
-	f = purple_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE);
-#else
-	f = purple_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE);
-#endif
+
+	silc_timezone(tz, sizeof(tz));
+	f = purple_request_field_string_new("timezone", _("Timezone (UTC)"), tz, FALSE);
 	purple_request_field_group_add_field(g, f);
 	purple_request_fields_add_group(fields, g);
 
@@ -861,12 +963,14 @@
 	if (f)
 		c = purple_request_field_string_get_value(f);
 
-	identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn,
-						 (char *)rn, (char *)e, (char *)o, (char *)c);
+	identifier = silc_pkcs_silc_encode_identifier((char *)un, (char *)hn,
+						      (char *)rn, (char *)e,
+						      (char *)o, (char *)c,
+						      NULL);
 
 	/* Create the key pair */
 	if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
-				  identifier, pass1, NULL, &public_key, NULL,
+				  identifier, pass1, &public_key, NULL,
 				  FALSE)) {
 		purple_notify_error(
 		     gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
@@ -941,10 +1045,10 @@
 	purple_request_fields_add_group(fields, g);
 
 	purple_request_fields(gc, _("Create New SILC Key Pair"),
-			    _("Create New SILC Key Pair"), NULL, fields,
-			    _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
-			    _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
-				gc->account, NULL, NULL, gc);
+			      _("Create New SILC Key Pair"), NULL, fields,
+			      _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
+			      _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
+			      gc->account, NULL, NULL, gc);
 
 	g_strfreev(u);
 	silc_free(hostname);
@@ -963,8 +1067,8 @@
         char prd[256];
 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
 	silc_change_private_key_passphrase(purple_account_get_string(gc->account,
-								   "private-key",
-								   prd), old, new);
+								     "private-key",
+								     prd), old, new);
 }
 
 static void
@@ -1028,49 +1132,46 @@
 
 static void
 silcpurple_send_im_resolved(SilcClient client,
-			  SilcClientConnection conn,
-			  SilcClientEntry *clients,
-			  SilcUInt32 clients_count,
-			  void *context)
+			    SilcClientConnection conn,
+			    SilcStatus status,
+			    SilcDList clients,
+			    void *context)
 {
 	PurpleConnection *gc = client->application;
 	SilcPurple sg = gc->proto_data;
 	SilcPurpleIM im = context;
 	PurpleConversation *convo;
-	char tmp[256], *nickname = NULL;
+	char tmp[256];
 	SilcClientEntry client_entry;
 #ifdef HAVE_SILCMIME_H
 	SilcDList list;
 #endif
 
 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
-							sg->account);
+						      sg->account);
 	if (!convo)
 		return;
 
 	if (!clients)
 		goto err;
 
-	if (clients_count > 1) {
-		silc_parse_userfqdn(im->nick, &nickname, NULL);
-
+	if (silc_dlist_count(clients) > 1) {
 		/* Find the correct one. The im->nick might be a formatted nick
 		   so this will find the correct one. */
 		clients = silc_client_get_clients_local(client, conn,
-							nickname, im->nick,
-							&clients_count);
+							im->nick, FALSE);
 		if (!clients)
 			goto err;
-		client_entry = clients[0];
-		silc_free(clients);
-	} else {
-		client_entry = clients[0];
 	}
 
+	silc_dlist_start(clients);
+	client_entry = silc_dlist_get(clients);
+
 #ifdef HAVE_SILCMIME_H
 	/* Check for images */
 	if (im->gflags & PURPLE_MESSAGE_IMAGES) {
-		list = silcpurple_image_message(im->message, (SilcUInt32 *)&im->flags);
+		list = silcpurple_image_message(im->message,
+						(SilcUInt32 *)(void *)&im->flags);
 		if (list) {
 			/* Send one or more MIME message.  If more than one, they
 			   are MIME fragments due to over large message */
@@ -1079,12 +1180,12 @@
 			silc_dlist_start(list);
 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
 				silc_client_send_private_message(client, conn,
-								 client_entry, im->flags,
-								 buf->data, buf->len,
-								 TRUE);
+								 client_entry, im->flags, NULL,
+								 buf->data,
+								 silc_buffer_len(buf));
 			silc_mime_partial_free(list);
 			purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
-				   im->message, 0, time(NULL));
+					     im->message, 0, time(NULL));
 			goto out;
 		}
 	}
@@ -1092,9 +1193,9 @@
 
 	/* Send the message */
 	silc_client_send_private_message(client, conn, client_entry, im->flags,
-					 (unsigned char *)im->message, im->message_len, TRUE);
+					 NULL, (unsigned char *)im->message, im->message_len);
 	purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
-			   im->message, 0, time(NULL));
+			     im->message, 0, time(NULL));
 	goto out;
 
  err:
@@ -1106,19 +1207,19 @@
 	g_free(im->nick);
 	g_free(im->message);
 	silc_free(im);
-	silc_free(nickname);
 }
 
 static int
 silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
-		 PurpleMessageFlags flags)
+		   PurpleMessageFlags flags)
 {
 	SilcPurple sg = gc->proto_data;
 	SilcClient client = sg->client;
 	SilcClientConnection conn = sg->conn;
-	SilcClientEntry *clients;
-	SilcUInt32 clients_count, mflags;
-	char *nickname, *msg, *tmp;
+	SilcDList clients;
+	SilcClientEntry client_entry;
+	SilcUInt32 mflags;
+	char *msg, *tmp;
 	int ret = 0;
 	gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
 #ifdef HAVE_SILCMIME_H
@@ -1141,14 +1242,9 @@
 		mflags |= SILC_MESSAGE_FLAG_ACTION;
 	} else if (strlen(msg) > 1 && msg[0] == '/') {
 		if (!silc_client_command_call(client, conn, msg + 1))
-			purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
-					_("Unknown command"));
-		g_free(tmp);
-		return 0;
-	}
-
-
-	if (!silc_parse_userfqdn(who, &nickname, NULL)) {
+			purple_notify_error(gc, _("Call Command"),
+					    _("Cannot call command"),
+					    _("Unknown command"));
 		g_free(tmp);
 		return 0;
 	}
@@ -1157,8 +1253,7 @@
 		mflags |= SILC_MESSAGE_FLAG_SIGNED;
 
 	/* Find client entry */
-	clients = silc_client_get_clients_local(client, conn, nickname, who,
-						&clients_count);
+	clients = silc_client_get_clients_local(client, conn, who, FALSE);
 	if (!clients) {
 		/* Resolve unknown user */
 		SilcPurpleIM im = silc_calloc(1, sizeof(*im));
@@ -1171,13 +1266,15 @@
 		im->message_len = strlen(im->message);
 		im->flags = mflags;
 		im->gflags = flags;
-		silc_client_get_clients(client, conn, nickname, NULL,
+		silc_client_get_clients(client, conn, who, NULL,
 					silcpurple_send_im_resolved, im);
-		silc_free(nickname);
 		g_free(tmp);
 		return 0;
 	}
 
+	silc_dlist_start(clients);
+	client_entry = silc_dlist_get(clients);
+
 #ifdef HAVE_SILCMIME_H
 	/* Check for images */
 	if (flags & PURPLE_MESSAGE_IMAGES) {
@@ -1191,27 +1288,25 @@
 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
 				ret =
 			 	silc_client_send_private_message(client, conn,
-								 clients[0], mflags,
-								 buf->data, buf->len,
-								 TRUE);
+								 client_entry, mflags, NULL,
+								 buf->data,
+								 silc_buffer_len(buf));
 			silc_mime_partial_free(list);
 			g_free(tmp);
-			silc_free(nickname);
-			silc_free(clients);
+			silc_client_list_free(client, conn, clients);
 			return ret;
 		}
 	}
 #endif
 
 	/* Send private message directly */
-	ret = silc_client_send_private_message(client, conn, clients[0],
-					       mflags,
+	ret = silc_client_send_private_message(client, conn, client_entry,
+					       mflags, NULL,
 					       (unsigned char *)msg,
-					       strlen(msg), TRUE);
+					       strlen(msg));
 
 	g_free(tmp);
-	silc_free(nickname);
-	silc_free(clients);
+	silc_client_list_free(client, conn, clients);
 	return ret;
 }
 
@@ -1219,7 +1314,6 @@
 static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
 	/* split this single menu building function back into the two
 	   original: one for buddies and one for chats */
-
 	if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		return silcpurple_chat_menu((PurpleChat *) node);
 	} else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
@@ -1548,7 +1642,7 @@
 		return PURPLE_CMD_RET_FAILED;
 
 	silc_client_command_call(sg->client, sg->conn, NULL,
-				 "QUIT", (args && args[0]) ? args[0] : "Download this: " PURPLE_WEBSITE, NULL);
+				 "QUIT", (args && args[0]) ? args[0] : "Download Pidgin: " PURPLE_WEBSITE, NULL);
 
 	return PURPLE_CMD_RET_OK;
 }
@@ -1729,75 +1823,75 @@
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
 	OPT_PROTO_PASSWORD_OPTIONAL,
 #endif
-	NULL,						/* user_splits */
-	NULL,						/* protocol_options */
+	NULL,					/* user_splits */
+	NULL,					/* protocol_options */
 #ifdef SILC_ATTRIBUTE_USER_ICON
 	{"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 #else
 	NO_BUDDY_ICONS,
 #endif
-	silcpurple_list_icon,			/* list_icon */
-	NULL,				/* list_emblems */
-	silcpurple_status_text,		/* status_text */
+	silcpurple_list_icon,	                /* list_icon */
+	NULL,					/* list_emblems */
+	silcpurple_status_text,			/* status_text */
 	silcpurple_tooltip_text,		/* tooltip_text */
-	silcpurple_away_states,		/* away_states */
-	silcpurple_blist_node_menu,	/* blist_node_menu */
+	silcpurple_away_states,			/* away_states */
+	silcpurple_blist_node_menu,		/* blist_node_menu */
 	silcpurple_chat_info,			/* chat_info */
-	silcpurple_chat_info_defaults,/* chat_info_defaults */
-	silcpurple_login,				/* login */
-	silcpurple_close,				/* close */
+	silcpurple_chat_info_defaults,	        /* chat_info_defaults */
+	silcpurple_login,			/* login */
+	silcpurple_close,			/* close */
 	silcpurple_send_im,			/* send_im */
 	silcpurple_set_info,			/* set_info */
-	NULL,						/* send_typing */
+	NULL,					/* send_typing */
 	silcpurple_get_info,			/* get_info */
-	silcpurple_set_status,		/* set_status */
+	silcpurple_set_status,			/* set_status */
 	silcpurple_idle_set,			/* set_idle */
 	silcpurple_change_passwd,		/* change_passwd */
 	silcpurple_add_buddy,			/* add_buddy */
-	NULL,						/* add_buddies */
+	NULL,					/* add_buddies */
 	silcpurple_remove_buddy,		/* remove_buddy */
-	NULL,						/* remove_buddies */
-	NULL,						/* add_permit */
-	NULL,						/* add_deny */
-	NULL,						/* rem_permit */
-	NULL,						/* rem_deny */
-	NULL,						/* set_permit_deny */
+	NULL,					/* remove_buddies */
+	NULL,					/* add_permit */
+	NULL,					/* add_deny */
+	NULL,					/* rem_permit */
+	NULL,					/* rem_deny */
+	NULL,					/* set_permit_deny */
 	silcpurple_chat_join,			/* join_chat */
-	NULL,						/* reject_chat */
+	NULL,					/* reject_chat */
 	silcpurple_get_chat_name,		/* get_chat_name */
-	silcpurple_chat_invite,		/* chat_invite */
-	silcpurple_chat_leave,		/* chat_leave */
-	NULL,						/* chat_whisper */
+	silcpurple_chat_invite,			/* chat_invite */
+	silcpurple_chat_leave,			/* chat_leave */
+	NULL,					/* chat_whisper */
 	silcpurple_chat_send,			/* chat_send */
 	silcpurple_keepalive,			/* keepalive */
-	NULL,						/* register_user */
-	NULL,						/* get_cb_info */
-	NULL,						/* get_cb_away */
-	NULL,						/* alias_buddy */
-	NULL,						/* group_buddy */
-	NULL,						/* rename_group */
-	NULL,						/* buddy_free */
-	NULL,						/* convo_closed */
-	NULL,						/* normalize */
+	NULL,					/* register_user */
+	NULL,					/* get_cb_info */
+	NULL,					/* get_cb_away */
+	NULL,					/* alias_buddy */
+	NULL,					/* group_buddy */
+	NULL,					/* rename_group */
+	NULL,					/* buddy_free */
+	NULL,					/* convo_closed */
+	NULL,					/* normalize */
 #ifdef SILC_ATTRIBUTE_USER_ICON
-	silcpurple_buddy_set_icon,			/* set_buddy_icon */
+	silcpurple_buddy_set_icon,		/* set_buddy_icon */
 #else
 	NULL,
 #endif
-	NULL,						/* remove_group */
-	NULL,						/* get_cb_real_name */
-	silcpurple_chat_set_topic,	/* set_chat_topic */
-	NULL,						/* find_blist_chat */
-	silcpurple_roomlist_get_list,	/* roomlist_get_list */
-	silcpurple_roomlist_cancel,	/* roomlist_cancel */
-	NULL,						/* roomlist_expand_category */
-	NULL,						/* can_receive_file */
+	NULL,					/* remove_group */
+	NULL,					/* get_cb_real_name */
+	silcpurple_chat_set_topic,		/* set_chat_topic */
+	NULL,					/* find_blist_chat */
+	silcpurple_roomlist_get_list,	        /* roomlist_get_list */
+	silcpurple_roomlist_cancel,		/* roomlist_cancel */
+	NULL,				        /* roomlist_expand_category */
+	NULL,					/* can_receive_file */
 	silcpurple_ftp_send_file,		/* send_file */
 	silcpurple_ftp_new_xfer,		/* new_xfer */
-	NULL,						/* offline_message */
+	NULL,					/* offline_message */
 	&silcpurple_wb_ops,			/* whiteboard_prpl_ops */
-	NULL,                       /* send_raw */
-	NULL,                       /* roomlist_room_serialize */
+	NULL,					/* send_raw */
+	NULL,				        /* roomlist_room_serialize */
 
 	/* padding */
 	NULL,
@@ -1811,29 +1905,29 @@
 	PURPLE_PLUGIN_MAGIC,
 	PURPLE_MAJOR_VERSION,
 	PURPLE_MINOR_VERSION,
-	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
-	NULL,                                             /**< ui_requirement */
-	0,                                                /**< flags          */
-	NULL,                                             /**< dependencies   */
-	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
+	PURPLE_PLUGIN_PROTOCOL,			/**< type           */
+	NULL,					/**< ui_requirement */
+	0,					/**< flags          */
+	NULL,					/**< dependencies   */
+	PURPLE_PRIORITY_DEFAULT,		/**< priority       */
 
-	"prpl-silc",                                      /**< id             */
-	"SILC",                                           /**< name           */
-	"1.0",                                            /**< version        */
+	"prpl-silc",				/**< id             */
+	"SILC",					/**< name           */
+	"1.1",					/**< version        */
 	/**  summary        */
 	N_("SILC Protocol Plugin"),
 	/**  description    */
 	N_("Secure Internet Live Conferencing (SILC) Protocol"),
-	"Pekka Riikonen",                                 /**< author         */
-	"http://silcnet.org/",                            /**< homepage       */
+	"Pekka Riikonen",			/**< author         */
+	"http://silcnet.org/",			/**< homepage       */
 
-	NULL,                                             /**< load           */
-	NULL,                                             /**< unload         */
-	NULL,                                             /**< destroy        */
+	NULL,					/**< load           */
+	NULL,					/**< unload         */
+	NULL,					/**< destroy        */
 
-	NULL,                                             /**< ui_info        */
-	&prpl_info,                                       /**< extra_info     */
-	NULL,                                             /**< prefs_info     */
+	NULL,					/**< ui_info        */
+	&prpl_info,				/**< extra_info     */
+	NULL,					/**< prefs_info     */
 	silcpurple_actions,
 
 	/* padding */
@@ -1860,18 +1954,18 @@
 
 	/* Account options */
 	option = purple_account_option_string_new(_("Connect server"),
-						"server",
-						"silc.silcnet.org");
+						  "server",
+						  "silc.silcnet.org");
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_int_new(_("Port"), "port", 706);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
 	option = purple_account_option_string_new(_("Public Key file"),
-						"public-key", tmp);
+						  "public-key", tmp);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
 	option = purple_account_option_string_new(_("Private Key file"),
-						"private-key", tmp);
+						  "private-key", tmp);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	for (i = 0; silc_default_ciphers[i].name; i++) {
@@ -1893,35 +1987,45 @@
 	option = purple_account_option_list_new(_("HMAC"), "hmac", list);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
+	option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"),
+						"pfs", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
 	option = purple_account_option_bool_new(_("Public key authentication"),
-					      "pubkey-auth", FALSE);
+						"pubkey-auth", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Reject watching by other users"),
-					      "reject-watch", FALSE);
+						"reject-watch", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Block invites"),
-					      "block-invites", FALSE);
+						"block-invites", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
-					      "block-ims", FALSE);
+						"block-ims", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Reject online status attribute requests"),
-					      "reject-attrs", FALSE);
+						"reject-attrs", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Block messages to whiteboard"),
-					      "block-wb", FALSE);
+						"block-wb", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Automatically open whiteboard"),
-					      "open-wb", FALSE);
+						"open-wb", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 	option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
-					      "sign-verify", FALSE);
+						"sign-verify", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	purple_prefs_remove("/plugins/prpl/silc");
 
+	silc_log_set_callback(SILC_LOG_ERROR, silcpurple_log_error, NULL);
 	silcpurple_register_commands();
 
+#if 0
+silc_log_debug(TRUE);
+silc_log_set_debug_string("*client*");
+#endif
+
 #ifdef _WIN32
 	silc_net_win32_init();
 #endif
--- a/libpurple/protocols/silc/silcpurple.h	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/silcpurple.h	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -66,6 +66,8 @@
 typedef struct SilcPurpleStruct {
 	SilcClient client;
 	SilcClientConnection conn;
+	SilcPublicKey public_key;
+	SilcPrivateKey private_key;
 
 	guint scheduler;
 	PurpleConnection *gc;
@@ -85,27 +87,29 @@
 } *SilcPurple;
 
 
+void silc_say(SilcClient client, SilcClientConnection conn,
+	      SilcClientMessageType type, char *msg, ...);
+SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn,
+				  SilcCommand command, SilcStatus status,
+				  SilcStatus error, void *context, va_list ap);
 gboolean silcpurple_check_silc_dir(PurpleConnection *gc);
-void silcpurple_chat_join_done(SilcClient client,
-			     SilcClientConnection conn,
-			     SilcClientEntry *clients,
-			     SilcUInt32 clients_count,
-			     void *context);
 const char *silcpurple_silcdir(void);
 const char *silcpurple_session_file(const char *account);
 void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn,
-				const char *name, SilcSocketType conn_type,
-				unsigned char *pk, SilcUInt32 pk_len,
-				SilcSKEPKType pk_type,
-				SilcVerifyPublicKey completion, void *context);
+				  const char *name,
+				  SilcConnectionType conn_type,
+				  SilcPublicKey public_key,
+				  SilcVerifyPublicKey completion,
+				  void *context);
 GList *silcpurple_buddy_menu(PurpleBuddy *buddy);
 void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 void silcpurple_send_buddylist(PurpleConnection *gc);
 void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
 void silcpurple_buddy_keyagr_request(SilcClient client,
-				   SilcClientConnection conn,
-				   SilcClientEntry client_entry,
-				   const char *hostname, SilcUInt16 port);
+				     SilcClientConnection conn,
+				     SilcClientEntry client_entry,
+				     const char *hostname, SilcUInt16 port,
+				     SilcUInt16 protocol);
 void silcpurple_idle_set(PurpleConnection *gc, int idle);
 void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full);
 char *silcpurple_status_text(PurpleBuddy *b);
@@ -140,7 +144,7 @@
 PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc);
 void silcpurple_roomlist_cancel(PurpleRoomlist *list);
 void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
-			       SilcBuffer channel_pubkeys);
+				 SilcDList channel_pubkeys);
 void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
 					 char **contactstr, char **langstr, char **devicestr,
 					 char **tzstr, char **geostr);
--- a/libpurple/protocols/silc/util.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/util.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2004 - 2005 Pekka Riikonen
+  Copyright (C) 2004 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 #include "imgstore.h"
@@ -206,22 +206,24 @@
 		if (errno == ENOENT) {
 			purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
 			if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
-					     SILCPURPLE_DEF_PKCS_LEN,
-					     file_public_key, file_private_key, NULL,
-					     (gc->password == NULL) ? "" : gc->password,
-						 NULL, NULL, NULL, FALSE)) {
-				purple_debug_error("silc", "Couldn't create key pair\n");
+						  SILCPURPLE_DEF_PKCS_LEN,
+						  file_public_key,
+						  file_private_key, NULL,
+						  (gc->password == NULL)
+						  ? "" : gc->password,
+						  NULL, NULL, FALSE)) {
+				purple_connection_error(gc, _("Cannot create SILC key pair\n"));
 				return FALSE;
 			}
 
 			if ((g_stat(file_public_key, &st)) == -1) {
 				purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
-					file_public_key, strerror(errno));
+						   file_public_key, strerror(errno));
 				return FALSE;
 			}
 		} else {
 			purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
-							 file_public_key, strerror(errno));
+					   file_public_key, strerror(errno));
 			return FALSE;
 		}
 	}
@@ -237,7 +239,7 @@
 	if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
 		if ((fstat(fd, &st)) == -1) {
 			purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
-							 file_private_key, strerror(errno));
+					   file_private_key, strerror(errno));
 			close(fd);
 			return FALSE;
 		}
@@ -246,18 +248,20 @@
 		if (errno == ENOENT) {
 			purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
 			if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
-					     SILCPURPLE_DEF_PKCS_LEN,
-					     file_public_key, file_private_key, NULL,
-					     (gc->password == NULL) ? "" : gc->password,
-						 NULL, NULL, NULL, FALSE)) {
-				purple_debug_error("silc", "Couldn't create key pair\n");
+						  SILCPURPLE_DEF_PKCS_LEN,
+						  file_public_key,
+						  file_private_key, NULL,
+						  (gc->password == NULL)
+						  ? "" : gc->password,
+						  NULL, NULL, FALSE)) {
+				purple_connection_error(gc, _("Cannot create SILC key pair\n"));
 				return FALSE;
 			}
 
 			if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
 				if ((fstat(fd, &st)) == -1) {
 					purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
-							 file_private_key, strerror(errno));
+							   file_private_key, strerror(errno));
 					close(fd);
 					return FALSE;
 				}
@@ -266,12 +270,12 @@
 			 * will set the permissions */
 			else if ((g_stat(file_private_key, &st)) == -1) {
 				purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
-					file_private_key, strerror(errno));
+						   file_private_key, strerror(errno));
 				return FALSE;
 			}
 		} else {
 			purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
-							 file_private_key, strerror(errno));
+					   file_private_key, strerror(errno));
 			return FALSE;
 		}
 	}
@@ -323,30 +327,29 @@
 #endif
 
 void silcpurple_show_public_key(SilcPurple sg,
-			      const char *name, SilcPublicKey public_key,
-			      GCallback callback, void *context)
+				const char *name, SilcPublicKey public_key,
+				GCallback callback, void *context)
 {
 	SilcPublicKeyIdentifier ident;
-	SilcPKCS pkcs;
+	SilcSILCPublicKey silc_pubkey;
 	char *fingerprint, *babbleprint;
 	unsigned char *pk;
 	SilcUInt32 pk_len, key_len = 0;
 	GString *s;
 	char *buf;
 
-	ident = silc_pkcs_decode_identifier(public_key->identifier);
-	if (!ident)
-		return;
+	/* We support showing only SILC public keys for now */
+	if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC)
+	  return;
+
+	silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
+	ident = &silc_pubkey->identifier;
+	key_len = silc_pkcs_public_key_get_len(public_key);
 
 	pk = silc_pkcs_public_key_encode(public_key, &pk_len);
 	fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
 	babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
 
-	if (silc_pkcs_alloc((unsigned char *)public_key->name, &pkcs)) {
-		key_len = silc_pkcs_public_key_set(pkcs, public_key);
-		silc_pkcs_free(pkcs);
-	}
-
 	s = g_string_new("");
 	if (ident->realname)
 		/* Hint for translators: Please check the tabulator width here and in
@@ -363,8 +366,10 @@
 		g_string_append_printf(s, _("Organization: \t%s\n"), ident->org);
 	if (ident->country)
 		g_string_append_printf(s, _("Country: \t%s\n"), ident->country);
-	g_string_append_printf(s, _("Algorithm: \t%s\n"), public_key->name);
+	g_string_append_printf(s, _("Algorithm: \t%s\n"), silc_pubkey->pkcs->name);
 	g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len);
+	if (ident->version)
+	  g_string_append_printf(s, _("Version: \t%s\n"), ident->version);
 	g_string_append_printf(s, "\n");
 	g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint);
 	g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
@@ -372,15 +377,14 @@
 	buf = g_string_free(s, FALSE);
 
 	purple_request_action(sg->gc, _("Public Key Information"),
-			    _("Public Key Information"),
-			    buf, 0, purple_connection_get_account(sg->gc),
-				NULL, NULL, context, 1, _("Close"), callback);
+			      _("Public Key Information"),
+			      buf, 0, purple_connection_get_account(sg->gc),
+			      NULL, NULL, context, 1, _("Close"), callback);
 
 	g_free(buf);
 	silc_free(fingerprint);
 	silc_free(babbleprint);
 	silc_free(pk);
-	silc_pkcs_free_identifier(ident);
 }
 
 SilcAttributePayload
@@ -400,7 +404,7 @@
 }
 
 void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
-			       SilcUInt32 buf_size)
+				 SilcUInt32 buf_size)
 {
 	memset(buf, 0, buf_size);
 	if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
@@ -435,7 +439,7 @@
 }
 
 void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
-				SilcUInt32 buf_size)
+				  SilcUInt32 buf_size)
 {
 	memset(buf, 0, buf_size);
 	if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
@@ -482,8 +486,8 @@
 
 void
 silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
-					 char **contactstr, char **langstr, char **devicestr,
-					 char **tzstr, char **geostr)
+		       char **contactstr, char **langstr, char **devicestr,
+		       char **tzstr, char **geostr)
 {
 	SilcAttributePayload attr;
 	SilcAttributeMood mood = 0;
@@ -620,23 +624,23 @@
 	ct = strrchr(filename, '.');
 	if (!ct)
 		return NULL;
-	else if (!g_ascii_strcasecmp(".png", ct))
+	else if (!strcasecmp(".png", ct))
 		return strdup("image/png");
-	else if (!g_ascii_strcasecmp(".jpg", ct))
+	else if (!strcasecmp(".jpg", ct))
 		return strdup("image/jpeg");
-	else if (!g_ascii_strcasecmp(".jpeg", ct))
+	else if (!strcasecmp(".jpeg", ct))
 		return strdup("image/jpeg");
-	else if (!g_ascii_strcasecmp(".gif", ct))
+	else if (!strcasecmp(".gif", ct))
 		return strdup("image/gif");
-	else if (!g_ascii_strcasecmp(".tiff", ct))
+	else if (!strcasecmp(".tiff", ct))
 		return strdup("image/tiff");
-	
+
 	return NULL;
 }
 
-/* Checks if message has images, and assembles MIME message if it has. 
-   If only one image is present, creates simple MIME image message.  If 
-   there are multiple images and/or text with images multipart MIME 
+/* Checks if message has images, and assembles MIME message if it has.
+   If only one image is present, creates simple MIME image message.  If
+   there are multiple images and/or text with images multipart MIME
    message is created. */
 
 SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
@@ -666,8 +670,9 @@
 			tmp = g_strndup(last, start - last);
 			text = purple_unescape_html(tmp);
 			g_free(tmp);
+
 			/* Add text */
-			silc_mime_add_data(p, text, strlen(text));
+			silc_mime_add_data(p, (const unsigned char *)text, strlen(text));
 			g_free(text);
 
 			if (!parts)
@@ -720,7 +725,7 @@
 				    "text/plain; charset=utf-8");
 
 		/* Add text */
-		silc_mime_add_data(p, tmp, strlen(tmp));
+		silc_mime_add_data(p, (const unsigned char *)tmp, strlen(tmp));
 		g_free(tmp);
 
 		if (!parts)
@@ -742,7 +747,7 @@
 		silc_mime_add_field(mime, "MIME-Version", "1.0");
 		g_snprintf(b, sizeof(b), "b%4X%4X",
 			   (unsigned int)time(NULL),
-			   silc_dlist_count(parts)); 
+			   silc_dlist_count(parts));
 		silc_mime_set_multipart(mime, "mixed", b);
 		silc_dlist_start(parts);
 		while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
--- a/libpurple/protocols/silc/wb.c	Sat Jun 09 16:39:00 2007 +0000
+++ b/libpurple/protocols/silc/wb.c	Sat Jun 09 17:31:28 2007 +0000
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2005 - 2007 Pekka Riikonen
 
   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
@@ -17,7 +17,7 @@
 
 */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "silcpurple.h"
 #include "wb.h"
@@ -30,7 +30,7 @@
   2 bytes	height
   4 bytes	brush color
   2 bytes	brush size
-  n bytes	data 
+  n bytes	data
 
   Data:
 
@@ -204,7 +204,7 @@
 		silc_buffer_pull(&buf, 8);
 		x = dx;
 		y = dy;
-		while (buf.len > 0) {
+		while (silc_buffer_len(&buf) > 0) {
 			ret = silc_buffer_unformat(&buf,
 						   SILC_STR_UI_INT(&dx),
 						   SILC_STR_UI_INT(&dy),
@@ -214,7 +214,7 @@
 			silc_buffer_pull(&buf, 8);
 
 			purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy,
-						  brush_color, brush_size);
+						    brush_color, brush_size);
 			x += dx;
 			y += dy;
 		}
@@ -253,8 +253,8 @@
 }
 
 static void
-silcpurple_wb_request(SilcClient client, const unsigned char *message, 
-		    SilcUInt32 message_len, SilcClientEntry sender, 
+silcpurple_wb_request(SilcClient client, const unsigned char *message,
+		    SilcUInt32 message_len, SilcClientEntry sender,
 		    SilcChannelEntry channel)
 {
 	char tmp[128];
@@ -406,16 +406,16 @@
 	/* Send the message */
 	if (wbs->type == 0) {
 		/* Private message */
-		silc_client_send_private_message(sg->client, sg->conn, 
-						 wbs->u.client, 
-						 SILC_MESSAGE_FLAG_DATA,
-						 packet->head, len, TRUE);
+		silc_client_send_private_message(sg->client, sg->conn,
+						 wbs->u.client,
+						 SILC_MESSAGE_FLAG_DATA, NULL,
+						 packet->head, len);
 	} else if (wbs->type == 1) {
 		/* Channel message. Channel private keys are not supported. */
 		silc_client_send_channel_message(sg->client, sg->conn,
 						 wbs->u.channel, NULL,
-						 SILC_MESSAGE_FLAG_DATA,
-						 packet->head, len, TRUE);
+						 SILC_MESSAGE_FLAG_DATA, NULL,
+						 packet->head, len);
 	}
 
 	silc_buffer_free(packet);
@@ -501,16 +501,16 @@
 	/* Send the message */
 	if (wbs->type == 0) {
 		/* Private message */
-		silc_client_send_private_message(sg->client, sg->conn, 
-						 wbs->u.client, 
-						 SILC_MESSAGE_FLAG_DATA,
-						 packet->head, len, TRUE);
+		silc_client_send_private_message(sg->client, sg->conn,
+						 wbs->u.client,
+						 SILC_MESSAGE_FLAG_DATA, NULL,
+						 packet->head, len);
 	} else if (wbs->type == 1) {
 		/* Channel message */
 		silc_client_send_channel_message(sg->client, sg->conn,
 						 wbs->u.channel, NULL,
-						 SILC_MESSAGE_FLAG_DATA,
-						 packet->head, len, TRUE);
+						 SILC_MESSAGE_FLAG_DATA, NULL,
+						 packet->head, len);
 	}
 
 	silc_buffer_free(packet);