diff libpurple/protocols/silc/silc.c @ 17806: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 0f4a562b38d1
children b142c090eaf7
line wrap: on
line diff
--- 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