updates.
[silc.git] / lib / silcclient / client.c
index 99d8114eb2e19d92eb427ce672d95a39557a0e62..b4f46104e8e13cac0048d511aac65aa3c5cb469b 100644 (file)
@@ -54,6 +54,9 @@ SilcClient silc_client_alloc(SilcClientOperations *ops, void *application)
 void silc_client_free(SilcClient client)
 {
   if (client) {
+    if (client->rng)
+      silc_rng_free(client->rng);
+
     silc_free(client);
   }
 }
@@ -76,7 +79,7 @@ int silc_client_init(SilcClient client)
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
   silc_rng_init(client->rng);
-  silc_math_primegen_init(); /* XXX */
+  silc_rng_global_init(client->rng);
 
   /* Register protocols */
   silc_client_protocols_register();
@@ -141,6 +144,7 @@ SilcClientConnection silc_client_add_connection(SilcClient client,
   conn->remote_host = strdup(hostname);
   conn->remote_port = port;
   conn->context = context;
+  conn->pending_commands = silc_dlist_init();
 
   /* Add the connection to connections table */
   for (i = 0; i < client->conns_count; i++)
@@ -165,6 +169,8 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 
   for (i = 0; i < client->conns_count; i++)
     if (client->conns[i] == conn) {
+      if (conn->pending_commands)
+       silc_dlist_uninit(conn->pending_commands);
       silc_free(conn);
       client->conns[i] = NULL;
     }
@@ -268,6 +274,7 @@ int silc_client_start_key_exchange(SilcClient client,
 
   conn->nickname = strdup(client->username);
   conn->sock->hostname = conn->remote_host;
+  conn->sock->ip = strdup(conn->remote_host);
   conn->sock->port = conn->remote_port;
 
   /* Allocate internal Key Exchange context. This is sent to the
@@ -376,7 +383,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     SILC_LOG_DEBUG(("Error during KE protocol"));
     silc_protocol_free(protocol);
@@ -408,13 +416,13 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
                                    &proto_ctx->auth_data_len))
     {
       /* XXX do AUTH_REQUEST resolcing with server */
-      proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
+      proto_ctx->auth_meth = SILC_AUTH_NONE;
     }
 
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   /* silc_free(ctx->keymat....); */
   sock->protocol = NULL;
@@ -447,7 +455,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     SILC_LOG_DEBUG(("Error during authentication protocol"));
     silc_protocol_free(protocol);
@@ -491,16 +500,11 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
   conn->remote_id_data_len = SILC_ID_SERVER_LEN;
 
-  /* Notify application of successful connection */
-  client->ops->connect(client, conn, TRUE);
-
   silc_protocol_free(protocol);
   if (ctx->auth_data)
     silc_free(ctx->auth_data);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
   silc_free(ctx);
   conn->sock->protocol = NULL;
 }
@@ -599,8 +603,7 @@ SILC_TASK_CALLBACK(silc_client_packet_process)
        return;
       }
       
-      client->ops->say(client, conn, "Connection closed: premature EOF");
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+      SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
       client->ops->disconnect(client, conn);
       silc_client_close_connection(client, sock);
       return;
@@ -648,8 +651,8 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   silc_client_packet_parse_type(client, sock, packet);
 
  out:
-  silc_buffer_clear(buffer);
-  silc_free(packet);
+  silc_buffer_clear(sock->inbuf);
+  silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
 
@@ -703,9 +706,21 @@ void silc_client_packet_parse_type(SilcClient client,
      * protocol and it will call the final callback.
      */
     if (sock->protocol) {
-      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
       sock->protocol->execute(client->timeout_queue, 0,
                              sock->protocol, sock->sock, 0, 0);
+
+      /* XXX We have only two protocols currently thus we know what this
+        failure indication is. */
+      if (buffer->len >= 4) {
+       unsigned int failure;
+
+       SILC_GET32_MSB(failure, buffer->data);
+
+       /* Notify application */
+       client->ops->failure(client, sock->user_data, sock->protocol,
+                            (void *)failure);
+      }
     }
     break;
   case SILC_PACKET_REJECT:
@@ -715,7 +730,7 @@ void silc_client_packet_parse_type(SilcClient client,
     /*
      * Received notify message 
      */
-    silc_client_notify_by_server(client, sock, buffer);
+    silc_client_notify_by_server(client, sock, packet);
     break;
 
   case SILC_PACKET_ERROR:
@@ -764,9 +779,12 @@ void silc_client_packet_parse_type(SilcClient client,
       SilcClientKEInternalContext *proto_ctx = 
        (SilcClientKEInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
       proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(client->timeout_queue, 0,
@@ -793,11 +811,14 @@ void silc_client_packet_parse_type(SilcClient client,
        (SilcClientKEInternalContext *)sock->protocol->context;
 
       if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+       silc_packet_context_free(proto_ctx->packet);
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
       proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(client->timeout_queue, 0,
@@ -816,22 +837,26 @@ void silc_client_packet_parse_type(SilcClient client,
        * user changes nickname but in that case the new ID is received
        * as command reply and not as this packet type.
        */
-      unsigned char *id_string;
-      unsigned short id_type;
-      
-      silc_buffer_unformat(buffer,
-                          SILC_STR_UI_SHORT(&id_type),
-                          SILC_STR_UI16_STRING_ALLOC(&id_string),
-                          SILC_STR_END);
-      
-      if ((SilcIdType)id_type != SILC_ID_CLIENT)
+      SilcIDPayload idp;
+
+      idp = silc_id_payload_parse(buffer);
+      if (!idp)
+       break;
+      if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
        break;
 
-      silc_client_receive_new_id(client, sock, id_string);
-      silc_free(id_string);
+      silc_client_receive_new_id(client, sock, idp);
+      silc_id_payload_free(idp);
       break;
     }
 
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat packet
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -891,7 +916,6 @@ void silc_client_packet_send(SilcClient client,
     packetdata.dst_id_len = 0;
     packetdata.dst_id_type = SILC_ID_NONE;
   }
-  packetdata.rng = client->rng;
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
   packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
@@ -946,6 +970,7 @@ void silc_client_packet_send_to_channel(SilcClient client,
   SilcCipher cipher;
   SilcHmac hmac;
   unsigned char *id_string;
+  unsigned int block_len;
 
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
@@ -956,16 +981,15 @@ void silc_client_packet_send_to_channel(SilcClient client,
   }
 
   /* Generate IV */
-  if (!channel->iv)
-    for (i = 0; i < 16; i++)
-      channel->iv[i] = silc_rng_get_byte(client->rng);
+  block_len = silc_cipher_get_block_len(channel->channel_key);
+  if (channel->iv[0] == '\0')
+    for (i = 0; i < block_len; i++) channel->iv[i] = silc_rng_get_byte(client->rng);
   else
-    silc_hash_make(client->md5hash, channel->iv, 16, channel->iv);
+    silc_hash_make(client->md5hash, channel->iv, block_len, channel->iv);
 
   /* Encode the channel payload */
-  payload = silc_channel_encode_payload(strlen(conn->nickname), conn->nickname,
-                                       data_len, data, 16, channel->iv, 
-                                       client->rng);
+  payload = silc_channel_payload_encode(data_len, data, block_len, 
+                                       channel->iv, client->rng);
   if (!payload) {
     client->ops->say(client, conn, 
                     "Error: Could not create packet to be sent to channel");
@@ -990,7 +1014,6 @@ void silc_client_packet_send_to_channel(SilcClient client,
   packetdata.dst_id = id_string;
   packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
   packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = client->rng;
   packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
   packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
@@ -1010,7 +1033,7 @@ void silc_client_packet_send_to_channel(SilcClient client,
   /* Encrypt payload of the packet. This is encrypted with the channel key. */
   channel->channel_key->cipher->encrypt(channel->channel_key->context,
                                        payload->data, payload->data,
-                                       payload->len - 16, /* -IV_LEN */
+                                       payload->len - block_len, /* -IV_LEN */
                                        channel->iv);
 
   /* Put the actual encrypted payload data into the buffer. */
@@ -1097,7 +1120,6 @@ void silc_client_packet_send_private_message(SilcClient client,
     packetdata.dst_id = conn->local_id_data;
   packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
   packetdata.dst_id_type = SILC_ID_CLIENT;
-  packetdata.rng = client->rng;
   packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
   packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
@@ -1160,8 +1182,7 @@ void silc_client_close_connection(SilcClient client,
   silc_net_close_connection(sock->sock);
 
   client->ops->say(client, sock->user_data,
-                  "Closed connection to host %s", sock->hostname ?
-                  sock->hostname : sock->ip);
+                  "Closed connection to host %s", sock->hostname);
 
   /* Free everything */
   if (sock->user_data) {
@@ -1190,6 +1211,8 @@ void silc_client_close_connection(SilcClient client,
       memset(conn->hmac_key, 0, conn->hmac_key_len);
       silc_free(conn->hmac_key);
     }
+    if (conn->pending_commands)
+      silc_dlist_uninit(conn->pending_commands);
 
     conn->sock = NULL;
     conn->remote_port = 0;
@@ -1203,6 +1226,7 @@ void silc_client_close_connection(SilcClient client,
     conn->local_id_data = NULL;
     conn->remote_host = NULL;
     conn->current_channel = NULL;
+    conn->pending_commands = NULL;
 
     silc_client_del_connection(client, conn);
   }
@@ -1250,22 +1274,626 @@ void silc_client_error_by_server(SilcClient client,
   silc_free(msg);
 }
 
+/* Called when notify is received and some async operation (such as command)
+   is required before processing the notify message. This calls again the
+   silc_client_notify_by_server and reprocesses the original notify packet. */
+
+static void silc_client_notify_by_server_pending(void *context)
+{
+  SilcPacketContext *p = (SilcPacketContext *)context;
+  silc_client_notify_by_server(p->context, p->sock, p);
+}
+
+/* Destructor for the pending command callback */
+
+static void silc_client_notify_by_server_destructor(void *context)
+{
+  silc_packet_context_free((SilcPacketContext *)context);
+}
+
+/* Resolve client information from server by Client ID. */
+
+static void silc_client_notify_by_server_resolve(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcPacketContext *packet,
+                                                SilcClientID *client_id)
+{
+  SilcPacketContext *p = silc_packet_context_dup(packet);
+  SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+
+  p->context = (void *)client;
+  p->sock = conn->sock;
+
+  silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                             silc_client_notify_by_server_destructor,
+                             silc_client_notify_by_server_pending, p);
+  silc_buffer_free(idp);
+}
+
 /* Received notify message from server */
 
 void silc_client_notify_by_server(SilcClient client,
                                  SilcSocketConnection sock,
-                                 SilcBuffer message)
+                                 SilcPacketContext *packet)
 {
-  char *msg;
+  SilcBuffer buffer = packet->buffer;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcNotifyPayload payload;
   SilcNotifyType type;
+  SilcArgumentPayload args;
 
-  SILC_GET16_MSB(type, message->data);
-  silc_buffer_pull(message, 2);
+  SilcClientID *client_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcClientEntry client_entry;
+  SilcClientEntry client_entry2;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcIDCacheEntry id_cache = NULL;
+  unsigned char *tmp;
+  unsigned int tmp_len, mode;
 
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
-  client->ops->notify(client, sock->user_data, type, msg);
-  silc_free(msg);
+  payload = silc_notify_payload_parse(buffer);
+  if (!payload)
+    goto out;
+
+  type = silc_notify_get_type(payload);
+  args = silc_notify_get_args(payload);
+  if (!args)
+    goto out;
+
+  switch(type) {
+  case SILC_NOTIFY_TYPE_NONE:
+    /* Notify application */
+    client->ops->notify(client, conn, type, 
+                       silc_argument_get_arg_type(args, 1, NULL));
+    break;
+
+  case SILC_NOTIFY_TYPE_INVITE:
+    /* 
+     * Someone invited me to a channel. Find Client and Channel entries
+     * for the application.
+     */
+    
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found query it */
+    client_entry = silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* XXX Will ALWAYS fail because currently we don't have way to resolve
+       channel information for channel that we're not joined to. */
+    /* XXX ways to fix: use (extended) LIST command, or define the channel
+       name to the notfy type when name resolving is not mandatory. */
+    /* Find channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      goto out;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_JOIN:
+    /*
+     * Someone has joined to a channel. Get their ID and nickname and
+     * cache them for later use.
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found query it */
+    client_entry = silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* If nickname or username hasn't been resolved, do so */
+    if (!client_entry->nickname || !client_entry->username) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Add client to channel */
+    chu = silc_calloc(1, sizeof(*chu));
+    chu->client = client_entry;
+    silc_list_add(channel->clients, chu);
+
+    /* XXX add support for multiple same nicks on same channel. Check
+       for them here */
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /*
+     * Someone has left a channel. We will remove it from the channel but
+     * we'll keep it in the cache in case we'll need it later.
+     */
+    
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Remove client from channel */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       silc_list_del(channel->clients, chu);
+       silc_free(chu);
+       break;
+      }
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /*
+     * Someone left SILC. We'll remove it from all channels and from cache.
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Remove from all channels */
+    silc_client_remove_from_channels(client, conn, client_entry);
+
+    /* Remove from cache */
+    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
+                          client_entry->id);
+
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, client_entry, tmp);
+
+    /* Free data */
+    if (client_entry->nickname)
+      silc_free(client_entry->nickname);
+    if (client_entry->server)
+      silc_free(client_entry->server);
+    if (client_entry->id)
+      silc_free(client_entry->id);
+    if (client_entry->send_key)
+      silc_cipher_free(client_entry->send_key);
+    if (client_entry->receive_key)
+      silc_cipher_free(client_entry->receive_key);
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /*
+     * Someone set the topic on a channel.
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, tmp, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    /*
+     * Someone changed their nickname. If we don't have entry for the new
+     * ID we will query it and return here after it's done. After we've
+     * returned we fetch the old entry and free it and notify the 
+     * application.
+     */
+
+    /* Get new Client ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Ignore my ID */
+    if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
+      break;
+
+    /* Find Client entry and if not found query it */
+    client_entry2 = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry2) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+    silc_free(client_id);
+
+    /* Get old Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find old Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Remove the old from cache */
+    silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
+                          client_entry->id);
+
+    /* Replace old ID entry with new one on all channels. */
+    silc_client_replace_from_channels(client, conn, client_entry,
+                                     client_entry2);
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, client_entry, client_entry2);
+
+    /* Free data */
+    if (client_entry->nickname)
+      silc_free(client_entry->nickname);
+    if (client_entry->server)
+      silc_free(client_entry->server);
+    if (client_entry->id)
+      silc_free(client_entry->id);
+    if (client_entry->send_key)
+      silc_cipher_free(client_entry->send_key);
+    if (client_entry->receive_key)
+      silc_cipher_free(client_entry->receive_key);
+    break;
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /*
+     * Someone changed a channel mode
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Save the new mode */
+    channel->mode = mode;
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, mode, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    /*
+     * Someone changed user's mode on a channel
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Get target Client ID */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    silc_free(client_id);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find target Client entry */
+    client_entry2 = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry2)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Save the mode */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       chu->mode = mode;
+       break;
+      }
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, mode, 
+                       client_entry2, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    /*
+     * Received Message of the day
+     */
+
+    /* Get motd */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    
+    /* Notify application */
+    client->ops->notify(client, conn, type, tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    /*
+     * Router has enforced a new ID to a channel. Let's change the old
+     * ID to the one provided here.
+     */
+
+    /* Get the old ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+    
+    /* Get the channel entry */
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Free the old ID */
+    silc_free(channel_id);
+    silc_free(channel->id);
+
+    /* Get the new ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel->id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel->id)
+      goto out;
+
+    id_cache->id = (void *)channel->id;
+
+    /* Notify application */
+    client->ops->notify(client, conn, type, channel, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    /*
+     * A client (maybe me) was kicked from a channel
+     */
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_idlist_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      break;
+
+    channel = (SilcChannelEntry)id_cache->context;
+
+    /* Get comment */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->ops->notify(client, conn, type, client_entry, tmp, channel);
+
+    /* If I was kicked from channel, remove the channel */
+    if (client_entry == conn->local_entry) {
+      if (conn->current_channel == channel)
+       conn->current_channel = NULL;
+      silc_idcache_del_by_id(conn->channel_cache, 
+                            SILC_ID_CHANNEL, channel->id);
+      silc_free(channel->channel_name);
+      silc_free(channel->id);
+      silc_free(channel->key);
+      silc_cipher_free(channel->channel_key);
+      silc_free(channel);
+    }
+    break;
+    
+  default:
+    break;
+  }
+
+ out:
+  silc_notify_payload_free(payload);
+  if (client_id)
+    silc_free(client_id);
+  if (channel_id)
+    silc_free(channel_id);
 }
 
 /* Processes the received new Client ID from server. Old Client ID is
@@ -1273,9 +1901,13 @@ void silc_client_notify_by_server(SilcClient client,
 
 void silc_client_receive_new_id(SilcClient client,
                                SilcSocketConnection sock,
-                               unsigned char *id_string)
+                               SilcIDPayload idp)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  int connecting = FALSE;
+
+  if (!conn->local_entry)
+    connecting = TRUE;
 
   /* Delete old ID from ID cache */
   silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
@@ -1283,21 +1915,35 @@ void silc_client_receive_new_id(SilcClient client,
   /* Save the new ID */
   if (conn->local_id)
     silc_free(conn->local_id);
-  conn->local_id = silc_id_str2id(id_string, SILC_ID_CLIENT);
   if (conn->local_id_data)
     silc_free(conn->local_id_data);
-  conn->local_id_data = 
-    silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
-  memcpy(conn->local_id_data, id_string, SILC_ID_CLIENT_LEN);
-  conn->local_id_data_len = SILC_ID_CLIENT_LEN;
+
+  conn->local_id = silc_id_payload_get_id(idp);
+  conn->local_id_data = silc_id_payload_get_data(idp);
+  conn->local_id_data_len = silc_id_payload_get_len(idp);;
+
   if (!conn->local_entry)
     conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
+
   conn->local_entry->nickname = conn->nickname;
+  if (!conn->local_entry->username) {
+    conn->local_entry->username = 
+      silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
+                 sizeof(conn->local_entry->username));
+    sprintf(conn->local_entry->username, "%s@%s", client->username,
+           client->hostname);
+  }
+  conn->local_entry->server = strdup(conn->remote_host);
   conn->local_entry->id = conn->local_id;
   
   /* Put it to the ID cache */
   silc_idcache_add(conn->client_cache, conn->nickname, SILC_ID_CLIENT,
                   conn->local_id, (void *)conn->local_entry, TRUE);
+
+  /* Notify application of successful connection. We do it here now that
+     we've received the Client ID and are allowed to send traffic. */
+  if (connecting)
+    client->ops->connect(client, conn, TRUE);
 }
 
 /* Processed received Channel ID for a channel. This is called when client
@@ -1306,87 +1952,103 @@ void silc_client_receive_new_id(SilcClient client,
 void silc_client_new_channel_id(SilcClient client,
                                SilcSocketConnection sock,
                                char *channel_name,
-                               unsigned int mode,
-                               unsigned char *id_string)
+                               unsigned int mode, SilcIDPayload idp)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcChannelID *id;
   SilcChannelEntry channel;
 
   SILC_LOG_DEBUG(("New channel ID"));
 
-  id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
   channel = silc_calloc(1, sizeof(*channel));
   channel->channel_name = channel_name;
-  channel->id = id;
+  channel->id = silc_id_payload_get_id(idp);
   channel->mode = mode;
+  silc_list_init(channel->clients, struct SilcChannelUserStruct, next);
+
   conn->current_channel = channel;
-  
+
   /* Put it to the ID cache */
   silc_idcache_add(conn->channel_cache, channel_name, SILC_ID_CHANNEL,
-                  (void *)id, (void *)channel, TRUE);
+                  (void *)channel->id, (void *)channel, TRUE);
 }
 
-/* Processes received key for channel. The received key will be used
-   to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible. 
-   This is the key that server has generated, this is not the channel
-   private key, it is entirely local setting. */
+/* Saves channel key from encoded `key_payload'. This is used when we
+   receive Channel Key Payload and when we are processing JOIN command 
+   reply. */
 
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet)
+void silc_client_save_channel_key(SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel)
 {
   unsigned char *id_string, *key, *cipher;
-  unsigned int key_len;
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  unsigned int tmp_len;
   SilcChannelID *id;
   SilcIDCacheEntry id_cache = NULL;
-  SilcChannelEntry channel;
   SilcChannelKeyPayload payload;
 
-  SILC_LOG_DEBUG(("Received key for channel"));
-  
-  payload = silc_channel_key_parse_payload(packet);
+  payload = silc_channel_key_payload_parse(key_payload);
   if (!payload)
     return;
 
-  id_string = silc_channel_key_get_id(payload, NULL);
+  id_string = silc_channel_key_get_id(payload, &tmp_len);
   if (!id_string) {
-    silc_channel_key_free_payload(payload);
+    silc_channel_key_payload_free(payload);
+    return;
+  }
+
+  id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
+  if (!id) {
+    silc_channel_key_payload_free(payload);
     return;
   }
-  id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
 
   /* Find channel. */
-  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                  SILC_ID_CHANNEL, &id_cache))
-    goto out;
-  
+  if (!channel) {
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
+                                    SILC_ID_CHANNEL, &id_cache))
+      goto out;
+    
+    /* Get channel entry */
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
   /* Save the key */
-  key = silc_channel_key_get_key(payload, &key_len);
+  key = silc_channel_key_get_key(payload, &tmp_len);
   cipher = silc_channel_key_get_cipher(payload, NULL);
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
+  memcpy(channel->key, key, tmp_len);
 
-  channel = (SilcChannelEntry)id_cache->context;
-  channel->key_len = key_len;
-  channel->key = silc_calloc(key_len, sizeof(*channel->key));
-  memcpy(channel->key, key, key_len);
-
-  silc_cipher_alloc(cipher, &channel->channel_key);
-  if (!channel->channel_key) {
-    client->ops->say(client, conn,
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    conn->client->ops->say(conn->client, conn,
                     "Cannot talk to channel: unsupported cipher %s", cipher);
     goto out;
   }
   channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, key_len);
+                                       key, channel->key_len);
 
   /* Client is now joined to the channel */
   channel->on_channel = TRUE;
 
  out:
   silc_free(id);
-  silc_channel_key_free_payload(payload);
+  silc_channel_key_payload_free(payload);
+}
+
+/* Processes received key for channel. The received key will be used
+   to protect the traffic on the channel for now on. Client must receive
+   the key to the channel before talking on the channel is possible. 
+   This is the key that server has generated, this is not the channel
+   private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcBuffer packet)
+{
+  SILC_LOG_DEBUG(("Received key for channel"));
+
+  /* Save the key */
+  silc_client_save_channel_key(sock->user_data, packet, NULL);
 }
 
 /* Process received message to a channel (or from a channel, really). This
@@ -1402,17 +2064,23 @@ void silc_client_channel_message(SilcClient client,
   SilcChannelPayload payload = NULL;
   SilcChannelID *id = NULL;
   SilcChannelEntry channel;
+  SilcChannelUser chu;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientID *client_id = NULL;
-  int i;
-  char *nickname;
+  int found = FALSE;
+  unsigned int block_len;
 
   /* Sanity checks */
   if (packet->dst_id_type != SILC_ID_CHANNEL)
     goto out;
 
-  client_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
+  client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                            SILC_ID_CLIENT);
+  if (!client_id)
+    goto out;
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+  if (!id)
+    goto out;
 
   /* Find the channel entry from channels on this connection */
   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
@@ -1423,37 +2091,30 @@ void silc_client_channel_message(SilcClient client,
 
   /* Decrypt the channel message payload. Push the IV out of the way,
      since it is not encrypted (after pushing buffer->tail has the IV). */
-  silc_buffer_push_tail(buffer, 16);
+  block_len = silc_cipher_get_block_len(channel->channel_key);
+  silc_buffer_push_tail(buffer, block_len);
   channel->channel_key->cipher->decrypt(channel->channel_key->context,
                                        buffer->data, buffer->data,
                                        buffer->len, buffer->tail);
-  silc_buffer_pull_tail(buffer, 16);
+  silc_buffer_pull_tail(buffer, block_len);
 
   /* Parse the channel message payload */
-  payload = silc_channel_parse_payload(buffer);
+  payload = silc_channel_payload_parse(buffer);
   if (!payload)
     goto out;
 
-  nickname = silc_channel_get_nickname(payload, NULL);
-  if (!nickname)
-    goto out;
-  
-  for (i = 0; i < channel->clients_count; i++) {
-    if (channel->clients[i] && 
-       !SILC_ID_CLIENT_COMPARE(channel->clients[i]->id, client_id))
-      nickname = channel->clients[i]->nickname;
+  /* Find client entry */
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
+      found = TRUE;
+      break;
+    }
   }
 
   /* Pass the message to application */
-  if (packet->src_id_type == SILC_ID_CLIENT) {
-    client->ops->channel_message(client, conn, nickname,
-                                channel->channel_name,
-                                silc_channel_get_data(payload, NULL));
-  } else {
-    /* Message from server */
-    /* XXX maybe this should be passed to app... */
-    client->ops->say(client, conn, "%s", silc_channel_get_data(payload, NULL));
-  }
+  client->ops->channel_message(client, conn, found ? chu->client : NULL,
+                              channel, silc_channel_get_data(payload, NULL));
 
  out:
   if (id)
@@ -1461,7 +2122,7 @@ void silc_client_channel_message(SilcClient client,
   if (client_id)
     silc_free(client_id);
   if (payload)
-    silc_channel_free_payload(payload);
+    silc_channel_payload_free(payload);
 }
 
 /* Private message received. This processes the private message and
@@ -1473,56 +2134,61 @@ void silc_client_private_message(SilcClient client,
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcBuffer buffer = packet->buffer;
+  SilcIDCacheEntry id_cache;
+  SilcClientID *remote_id = NULL;
+  SilcClientEntry remote_client;
   unsigned short nick_len;
-  unsigned char *nickname, *message;
+  unsigned char *nickname, *message = NULL;
+  int ret;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    goto out;
 
   /* Get nickname */
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(buffer, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
+
   silc_buffer_pull(buffer, 2 + nick_len);
-     
+
   message = silc_calloc(buffer->len + 1, sizeof(char));
   memcpy(message, buffer->data, buffer->len);
 
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    goto out;
+
+  /* Check whether we know this client already */
+  if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
+                                  SILC_ID_CLIENT, &id_cache))
+    {
+      /* Allocate client entry */
+      remote_client = silc_calloc(1, sizeof(*remote_client));
+      remote_client->id = remote_id;
+      silc_parse_nickname(nickname, &remote_client->nickname, 
+                         &remote_client->server, &remote_client->num);
+      
+      /* Save the client to cache */
+      silc_idcache_add(conn->client_cache, remote_client->nickname,
+                      SILC_ID_CLIENT, remote_client->id, remote_client, 
+                      TRUE);
+    } else {
+      remote_client = (SilcClientEntry)id_cache->context;
+    }
+
   /* Pass the private message to application */
-  client->ops->private_message(client, conn, nickname, message);
+  client->ops->private_message(client, conn, remote_client, message);
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
   if (conn->away && conn->away->away) {
-    SilcClientID *remote_id;
-    SilcClientEntry remote_client;
-    SilcIDCacheEntry id_cache;
-
-    if (packet->src_id_type != SILC_ID_CLIENT)
-      goto out;
-
-    remote_id = silc_id_str2id(packet->src_id, SILC_ID_CLIENT);
-    if (!remote_id)
-      goto out;
-
+    /* If it's me, ignore */
     if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
       goto out;
 
-    /* Check whether we know this client already */
-    if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
-                                    SILC_ID_CLIENT, &id_cache))
-      {
-       /* Allocate client entry */
-       remote_client = silc_calloc(1, sizeof(*remote_client));
-       remote_client->id = remote_id;
-       remote_client->nickname = strdup(nickname);
-
-       /* Save the client to cache */
-       silc_idcache_add(conn->client_cache, remote_client->nickname,
-                        SILC_ID_CLIENT, remote_client->id, remote_client, 
-                        TRUE);
-      } else {
-       silc_free(remote_id);
-       remote_client = (SilcClientEntry)id_cache->context;
-      }
-
     /* Send the away message */
     silc_client_packet_send_private_message(client, sock, remote_client,
                                            conn->away->away,
@@ -1530,7 +2196,172 @@ void silc_client_private_message(SilcClient client,
   }
 
  out:
-  memset(message, 0, buffer->len);
-  silc_free(message);
+  if (remote_id)
+    silc_free(remote_id);
+
+  if (message) {
+    memset(message, 0, buffer->len);
+    silc_free(message);
+  }
   silc_free(nickname);
 }
+
+/* Removes a client entry from all channel it has joined. This really is
+   a performance killer (client_entry should have pointers to channel 
+   entry list). */
+
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+
+  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
+                              SILC_ID_CHANNEL, &list))
+    return;
+
+  silc_idcache_list_first(list, &id_cache);
+  channel = (SilcChannelEntry)id_cache->context;
+  
+  while (channel) {
+    
+    /* Remove client from channel */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == client_entry) {
+       silc_list_del(channel->clients, chu);
+       silc_free(chu);
+       break;
+      }
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+    
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  silc_idcache_list_free(list);
+}
+
+/* Replaces `old' client entries from all channels to `new' client entry.
+   This can be called for example when nickname changes and old ID entry
+   is replaced from ID cache with the new one. If the old ID entry is only
+   updated, then this fucntion needs not to be called. */
+
+void silc_client_replace_from_channels(SilcClient client, 
+                                      SilcClientConnection conn,
+                                      SilcClientEntry old,
+                                      SilcClientEntry new)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+
+  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
+                              SILC_ID_CHANNEL, &list))
+    return;
+
+  silc_idcache_list_first(list, &id_cache);
+  channel = (SilcChannelEntry)id_cache->context;
+  
+  while (channel) {
+    
+    /* Replace client entry */
+    silc_list_start(channel->clients);
+    while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+      if (chu->client == old) {
+       chu->client = new;
+       break;
+      }
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+    
+    channel = (SilcChannelEntry)id_cache->context;
+  }
+
+  silc_idcache_list_free(list);
+}
+
+/* Parses mode mask and returns the mode as string. */
+
+char *silc_client_chmode(unsigned int mode)
+{
+  char string[20];
+
+  if (!mode)
+    return NULL;
+
+  memset(string, 0, sizeof(string));
+
+  if (mode & SILC_CHANNEL_MODE_PRIVATE)
+    strncat(string, "p", 1);
+
+  if (mode & SILC_CHANNEL_MODE_SECRET)
+    strncat(string, "s", 1);
+
+  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
+    strncat(string, "k", 1);
+
+  if (mode & SILC_CHANNEL_MODE_INVITE)
+    strncat(string, "i", 1);
+
+  if (mode & SILC_CHANNEL_MODE_TOPIC)
+    strncat(string, "t", 1);
+
+  if (mode & SILC_CHANNEL_MODE_ULIMIT)
+    strncat(string, "l", 1);
+
+  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
+    strncat(string, "a", 1);
+
+  /* Rest of mode is ignored */
+
+  return strdup(string);
+}
+
+/* Parses channel user mode mask and returns te mode as string */
+
+char *silc_client_chumode(unsigned int mode)
+{
+  char string[4];
+
+  if (!mode)
+    return NULL;
+
+  memset(string, 0, sizeof(string));
+
+  if (mode & SILC_CHANNEL_UMODE_CHANFO)
+    strncat(string, "f", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_CHANOP)
+    strncat(string, "o", 1);
+
+  return strdup(string);
+}
+
+/* Parses channel user mode and returns it as special mode character. */
+
+char *silc_client_chumode_char(unsigned int mode)
+{
+  char string[4];
+
+  if (!mode)
+    return NULL;
+
+  memset(string, 0, sizeof(string));
+
+  if (mode & SILC_CHANNEL_UMODE_CHANFO)
+    strncat(string, "*", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_CHANOP)
+    strncat(string, "@", 1);
+
+  return strdup(string);
+}