diff libpurple/protocols/silc/ops.c @ 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 5e47b3427b28
children 9a96d8711303
line wrap: on
line diff
--- 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
 };