Added SILC Server library.
[silc.git] / lib / silcclient / client_notify.c
index 09ca7735ed3174017058ec1d8c74d96cc4c0dc1b..c9b6ab632376fb2d175d55a014162bd25942e9a5 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  client_notify.c 
+  client_notify.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2005 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
    important packets sent by the server. They tell different things to the
    client such as nick changes, mode changes etc. */
 
-#include "clientlibincludes.h"
+#include "silc.h"
+#include "silcclient.h"
 #include "client_internal.h"
 
+/* Context used for resolving client, channel and server info. */
 typedef struct {
-  SilcPacketContext *packet;
+  void *packet;
   void *context;
   SilcSocketConnection sock;
 } *SilcClientNotifyResolve;
 
+SILC_TASK_CALLBACK(silc_client_notify_check_client)
+{
+  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
+  SilcClient client = res->context;
+  SilcClientConnection conn = res->sock->user_data;
+  SilcClientID *client_id = res->packet;
+  silc_client_get_client_by_id_resolve(client, conn, client_id,
+                                      NULL, NULL, NULL);
+  silc_free(client_id);
+  silc_socket_free(res->sock);
+  silc_free(res);
+}
+
+SILC_TASK_CALLBACK(silc_client_notify_del_client_cb)
+{
+  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
+  SilcClient client = res->context;
+  SilcClientConnection conn = res->sock->user_data;
+  SilcClientID *client_id = res->packet;
+  SilcClientEntry client_entry;
+  client_entry = silc_client_get_client_by_id(client, conn, client_id);
+  if (client_entry)
+    silc_client_del_client(client, conn, client_entry);
+  silc_free(client_id);
+  silc_socket_free(res->sock);
+  silc_free(res);
+}
+
 /* 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. */
@@ -37,18 +67,13 @@ typedef struct {
 static void silc_client_notify_by_server_pending(void *context, void *context2)
 {
   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
-  SilcClientCommandReplyContext reply = 
+  SilcClientCommandReplyContext reply =
     (SilcClientCommandReplyContext)context2;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (reply) {
-    SilcCommandStatus status;
-    unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
-    SILC_GET16_MSB(status, tmp);
-    if (status != SILC_STATUS_OK)
-      goto out;
-  }
+  if (reply && !silc_command_get_status(reply->payload, NULL, NULL))
+    goto out;
 
   silc_client_notify_by_server(res->context, res->sock, res->packet);
 
@@ -58,27 +83,101 @@ static void silc_client_notify_by_server_pending(void *context, void *context2)
   silc_free(res);
 }
 
-/* Resolve client information from server by Client ID. */
+/* Resets the channel entry's resolve_cmd_ident after whatever-thing
+   was resolved is completed. */
+
+static void silc_client_channel_cond(void *context, void *context2)
+{
+  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
+  SilcClient client = res->context;
+  SilcClientConnection conn = res->sock->user_data;
+  SilcChannelID *channel_id = res->packet;
+  SilcChannelEntry channel;
+  channel = silc_client_get_channel_by_id(client, conn, channel_id);
+  if (channel)
+    channel->resolve_cmd_ident = 0;
+  silc_free(channel_id);
+  silc_socket_free(res->sock);
+  silc_free(res);
+}
+
+/* Function that starts waiting for the `cmd_ident' to arrive and
+   marks the channel info being resolved.  */
+
+static void silc_client_channel_set_wait(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel,
+                                        SilcUInt16 cmd_ident)
+{
+  SilcClientNotifyResolve res;
+
+  if (!channel->resolve_cmd_ident) {
+    res = silc_calloc(1, sizeof(*res));
+    res->context = client;
+    res->sock = silc_socket_dup(conn->sock);
+    res->packet = silc_id_dup(channel->id, SILC_ID_CHANNEL);
+    silc_client_command_pending(conn, SILC_COMMAND_NONE, cmd_ident,
+                               silc_client_channel_cond, res);
+    channel->resolve_cmd_ident = cmd_ident;
+  }
+}
+
+/* Attaches to the channel's resolving cmd ident and calls the
+   notify handling with `packet' after it's received. */
+
+static void silc_client_channel_wait(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcChannelEntry channel,
+                                    SilcPacketContext *packet)
+{
+  SilcClientNotifyResolve res;
+
+  if (!channel->resolve_cmd_ident)
+    return;
+
+  res = silc_calloc(1, sizeof(*res));
+  res->packet = silc_packet_context_dup(packet);
+  res->context = client;
+  res->sock = silc_socket_dup(conn->sock);
+
+  silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                             channel->resolve_cmd_ident,
+                             silc_client_notify_by_server_pending, res);
+}
+
+/* Resolve client, channel or server information. */
 
 static void silc_client_notify_by_server_resolve(SilcClient client,
                                                 SilcClientConnection conn,
                                                 SilcPacketContext *packet,
-                                                SilcClientID *client_id)
+                                                SilcIdType id_type,
+                                                void *id)
 {
   SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
-  SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  SilcBuffer idp = silc_id_payload_encode(id, id_type);
 
   res->packet = silc_packet_context_dup(packet);
   res->context = client;
   res->sock = silc_socket_dup(conn->sock);
 
-  silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
-                              silc_client_command_reply_whois_i, 0,
-                              ++conn->cmd_ident);
-  silc_client_command_send(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_pending, res);
+  /* For client resolving use WHOIS, and otherwise use IDENTIFY */
+  if (id_type == SILC_ID_CLIENT) {
+    silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
+                                silc_client_command_reply_whois_i, 0,
+                                ++conn->cmd_ident);
+    silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                            1, 4, idp->data, idp->len);
+    silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                               silc_client_notify_by_server_pending, res);
+  } else {
+    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                silc_client_command_reply_identify_i, 0,
+                                ++conn->cmd_ident);
+    silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                            conn->cmd_ident, 1, 5, idp->data, idp->len);
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                               silc_client_notify_by_server_pending, res);
+  }
   silc_buffer_free(idp);
 }
 
@@ -94,17 +193,18 @@ void silc_client_notify_by_server(SilcClient client,
   SilcNotifyType type;
   SilcArgumentPayload args;
 
-  SilcIDPayload idp;
+  void *id;
+  SilcIdType id_type;
   SilcClientID *client_id = NULL;
   SilcChannelID *channel_id = NULL;
   SilcServerID *server_id = NULL;
-  SilcClientEntry client_entry;
-  SilcClientEntry client_entry2;
+  SilcClientEntry client_entry = NULL;
+  SilcClientEntry client_entry2 = NULL;
   SilcChannelEntry channel;
   SilcChannelUser chu;
   SilcServerEntry server;
   unsigned char *tmp;
-  uint32 tmp_len, mode;
+  SilcUInt32 tmp_len, mode;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -120,16 +220,16 @@ void silc_client_notify_by_server(SilcClient client,
   switch(type) {
   case SILC_NOTIFY_TYPE_NONE:
     /* Notify application */
-    client->internal->ops->notify(client, conn, type, 
+    client->internal->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.
      */
-    
+
     SILC_LOG_DEBUG(("Notify: INVITE"));
 
     /* Get Channel ID */
@@ -137,7 +237,7 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
 
@@ -149,14 +249,15 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Find Client entry and if not found query it */
     client_entry = silc_client_get_client_by_id(client, conn, client_id);
     if (!client_entry) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      silc_client_notify_by_server_resolve(client, conn, packet,
+                                          SILC_ID_CLIENT, client_id);
       goto out;
     }
 
@@ -166,7 +267,7 @@ void silc_client_notify_by_server(SilcClient client,
       goto out;
 
     /* Notify application */
-    client->internal->ops->notify(client, conn, type, channel, tmp, 
+    client->internal->ops->notify(client, conn, type, channel, tmp,
                                  client_entry);
     break;
 
@@ -178,49 +279,72 @@ void silc_client_notify_by_server(SilcClient client,
 
     SILC_LOG_DEBUG(("Notify: JOIN"));
 
+    /* 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, NULL);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
     /* 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);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Find Client entry and if not found query it */
     client_entry = silc_client_get_client_by_id(client, conn, client_id);
     if (!client_entry) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      silc_client_channel_set_wait(client, conn, channel,
+                                  conn->cmd_ident + 1);
+      silc_client_notify_by_server_resolve(client, conn, packet,
+                                          SILC_ID_CLIENT, client_id);
       goto out;
     }
 
     /* If nickname or username hasn't been resolved, do so */
     if (!client_entry->nickname || !client_entry->username) {
       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
-       client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+       /* Attach to existing resolving */
+       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+       res->packet = silc_packet_context_dup(packet);
+       res->context = client;
+       res->sock = silc_socket_dup(conn->sock);
+       silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                   client_entry->resolve_cmd_ident,
+                                   silc_client_notify_by_server_pending,
+                                   res);
        goto out;
       }
+
+      /* Do new resolving */
+      silc_client_channel_set_wait(client, conn, channel,
+                                  conn->cmd_ident + 1);
+      silc_client_notify_by_server_resolve(client, conn, packet,
+                                          SILC_ID_CLIENT, client_id);
       client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      client_entry->resolve_cmd_ident = conn->cmd_ident;
       goto out;
     } else {
       if (client_entry != conn->local_entry)
        silc_client_nickname_format(client, conn, client_entry);
     }
 
-    /* Get Channel ID */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp)
+    /* If information is being resolved for this channel, wait for it */
+    if (channel->resolve_cmd_ident) {
+      silc_client_channel_wait(client, conn, channel, packet);
       goto out;
-
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!channel_id)
-      goto out;
-
-    /* Get channel entry */
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+    }
 
     /* Join the client to channel */
     if (!silc_client_on_channel(channel, client_entry)) {
@@ -242,7 +366,7 @@ void silc_client_notify_by_server(SilcClient client,
      * 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.
      */
-    
+
     SILC_LOG_DEBUG(("Notify: LEAVE"));
 
     /* Get Client ID */
@@ -250,12 +374,12 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Find Client entry */
-    client_entry = 
+    client_entry =
       silc_client_get_client_by_id(client, conn, client_id);
     if (!client_entry)
       goto out;
@@ -277,6 +401,21 @@ void silc_client_notify_by_server(SilcClient client,
       silc_free(chu);
     }
 
+    /* Some client implementations actually quit network by first doing
+       LEAVE and then immediately SIGNOFF.  We'll check for this by doing
+       check for the client after 5 - 34 seconds.  If it is not valid after
+       that we'll remove the client from cache. */
+    if (!silc_hash_table_count(client_entry->channels)) {
+      SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+      res->context = client;
+      res->sock = silc_socket_dup(conn->sock);
+      res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
+      silc_schedule_task_add(client->schedule, conn->sock->sock,
+                            silc_client_notify_check_client, res,
+                            (5 + (silc_rng_get_rn16(client->rng) % 29)),
+                            0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    }
+
     /* 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. */
@@ -295,22 +434,16 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Find Client entry */
-    client_entry = 
+    client_entry =
       silc_client_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_context(conn->client_cache, client_entry);
-
     /* Get signoff message */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
     if (tmp_len > 128)
@@ -319,6 +452,12 @@ void silc_client_notify_by_server(SilcClient client,
     /* Notify application */
     client->internal->ops->notify(client, conn, type, client_entry, tmp);
 
+    /* Remove from all channels */
+    silc_client_remove_from_channels(client, conn, client_entry);
+
+    /* Remove from cache */
+    silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
+
     /* Free data */
     silc_client_del_client_entry(client, conn, client_entry);
     break;
@@ -330,62 +469,80 @@ void silc_client_notify_by_server(SilcClient client,
 
     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
 
-    /* Get Client ID */
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Get ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-
-    idp = silc_id_payload_parse(tmp, tmp_len);
-    if (!idp)
+    id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+    if (!id)
       goto out;
 
     /* Find Client entry */
-    if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
-      client_id = silc_id_payload_parse_id(tmp, tmp_len);
-      if (!client_id) {
-       silc_id_payload_free(idp);
-       goto out;
-      }
-
+    if (id_type == SILC_ID_CLIENT) {
       /* Find Client entry */
-      client_entry = 
-       silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry)
-       goto out;
-    } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
-      server_id = silc_id_payload_parse_id(tmp, tmp_len);
-      if (!server_id) {
-       silc_id_payload_free(idp);
+      client_id = id;
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry) {
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CLIENT, client_id);
        goto out;
       }
-      
+    } else if (id_type == SILC_ID_SERVER) {
+      /* Find Server entry */
+      server_id = id;
       server = silc_client_get_server_by_id(client, conn, server_id);
       if (!server) {
-       silc_id_payload_free(idp);
-       silc_free(server_id);
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_SERVER, server_id);
+       server = silc_client_add_server(client, conn, NULL, NULL, server_id);
+       if (!server)
+         goto out;
+
+       server->resolve_cmd_ident = conn->cmd_ident;
+       server_id = NULL;
        goto out;
       }
-      
+
+      /* If entry being resoled, wait for it before processing this notify */
+      if (server->resolve_cmd_ident) {
+       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+       res->packet = silc_packet_context_dup(packet);
+       res->context = client;
+       res->sock = silc_socket_dup(conn->sock);
+       silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                   server->resolve_cmd_ident,
+                                   silc_client_notify_by_server_pending, res);
+       goto out;
+      }
+
       /* Save the pointer to the client_entry pointer */
       client_entry = (SilcClientEntry)server;
-      silc_free(server_id);
     } else {
-      channel_id = silc_id_payload_parse_id(tmp, tmp_len);
-      if (!channel_id) {
-       silc_id_payload_free(idp);
-       goto out;
-      }
-      
-      channel = silc_client_get_channel_by_id(client, conn, channel_id);
-      if (!channel) {
-       silc_id_payload_free(idp);
-       silc_free(channel_id);
+      /* Find Channel entry */
+      silc_free(channel_id);
+      channel_id = id;
+      client_entry = (SilcClientEntry)
+       silc_client_get_channel_by_id(client, conn, channel_id);
+      if (!client_entry) {
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CHANNEL, channel_id);
        goto out;
       }
-      
-      /* Save the pointer to the client_entry pointer */
-      client_entry = (SilcClientEntry)channel;
-      silc_free(channel_id);
     }
 
     /* Get topic */
@@ -393,30 +550,30 @@ void silc_client_notify_by_server(SilcClient client,
     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)
+    /* If information is being resolved for this channel, wait for it */
+    if (channel->resolve_cmd_ident) {
+      silc_client_channel_wait(client, conn, channel, packet);
       goto out;
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+    }
+
+    if (tmp) {
+      silc_free(channel->topic);
+      channel->topic = silc_memdup(tmp, strlen(tmp));
+    }
 
     /* 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->internal->ops->notify(client, conn, type, 
-                                 silc_id_payload_get_type(idp),
+    client->internal->ops->notify(client, conn, type, id_type,
                                  client_entry, tmp, channel);
 
-    silc_id_payload_free(idp);
     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 
+     * returned we fetch the old entry and free it and notify the
      * application.
      */
 
@@ -427,12 +584,12 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Ignore my ID */
-    if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
+    if (conn->local_id && SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
       break;
 
     /* Find old Client entry */
@@ -440,6 +597,19 @@ void silc_client_notify_by_server(SilcClient client,
     if (!client_entry)
       goto out;
     silc_free(client_id);
+    client_id = NULL;
+
+    /* Wait for resolving if necessary */
+    if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+      SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+      res->packet = silc_packet_context_dup(packet);
+      res->context = client;
+      res->sock = silc_socket_dup(conn->sock);
+      silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                 client_entry->resolve_cmd_ident,
+                                 silc_client_notify_by_server_pending, res);
+      goto out;
+    }
 
     client_entry->valid = FALSE;
 
@@ -448,145 +618,262 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
-    /* Find Client entry and if not found resolve it */
-    client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry2) {
-      /* Resolve the entry information */
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
-
-      /* Add the new entry even though we resolved it. This is because we
-        want to replace the old entry with the new entry here right now. */
-      client_entry2 = 
-       silc_client_add_client(client, conn, NULL, NULL, NULL, 
-                              silc_id_dup(client_id, SILC_ID_CLIENT), 
-                              client_entry->mode);
-
-      /* Replace old ID entry with new one on all channels. */
-      silc_client_replace_from_channels(client, conn, client_entry,
-                                       client_entry2);
-    } else {
-      if (client_entry2 != conn->local_entry)
-       silc_client_nickname_format(client, conn, client_entry2);
+    /* Take the nickname */
+    tmp = silc_argument_get_arg_type(args, 3, NULL);
+    if (!tmp)
+      goto out;
 
-      /* Remove the old from cache */
-      silc_idcache_del_by_context(conn->client_cache, client_entry);
+    /* Check whether nickname changed at all.  It is possible that nick
+       change notify is received but nickname didn't change, only the
+       ID changes.  Check whether the hashes in the Client ID match, if
+       they do nickname didn't change. */
+    if (SILC_ID_COMPARE_HASH(client_entry->id, client_id) &&
+       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
+      /* Nickname didn't change.  Update only Client ID. */
+
+      /* Normalize nickname */
+      tmp = silc_identifier_check(tmp, strlen(tmp),
+                                 SILC_STRING_UTF8, 128, NULL);
+      if (!tmp)
+       goto out;
 
-      /* Replace old ID entry with new one on all channels. */
-      silc_client_replace_from_channels(client, conn, client_entry,
-                                       client_entry2);
+      silc_idcache_del_by_context(conn->internal->client_cache,
+                                 client_entry);
+      silc_free(client_entry->id);
+      client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
+      silc_idcache_add(conn->internal->client_cache, tmp,
+                      client_entry->id, client_entry, 0, NULL);
 
       /* Notify application */
-      client->internal->ops->notify(client, conn, type, 
-                                   client_entry, client_entry2);
-
-      /* Free data */
-      silc_client_del_client_entry(client, conn, client_entry);
+      client->internal->ops->notify(client, conn, type,
+                                   client_entry, client_entry);
+      break;
     }
-    break;
 
-  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
-    /*
-     * Someone changed a channel mode
-     */
+    /* Create new client entry, and save all old information with the
+       new nickname and client ID */
+    client_entry2 = silc_client_add_client(client, conn, NULL, NULL,
+                                          client_entry->realname,
+                                          silc_id_dup(client_id,
+                                                      SILC_ID_CLIENT), 0);
+    if (!client_entry2)
+      goto out;
 
-    SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
+    if (client_entry->server)
+      client_entry2->server = strdup(client_entry->server);
+    if (client_entry->username)
+      client_entry2->username = strdup(client_entry->username);
+    if (client_entry->hostname)
+      client_entry2->hostname = strdup(client_entry->hostname);
+    client_entry2->fingerprint = client_entry->fingerprint;
+    client_entry2->fingerprint_len = client_entry->fingerprint_len;
+    client_entry->fingerprint = NULL;
+    client_entry->fingerprint_len = 0;
+    silc_client_update_client(client, conn, client_entry2, tmp, NULL, NULL,
+                             client_entry->mode);
+
+    /* Remove the old from cache */
+    silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
+
+    /* Replace old ID entry with new one on all channels. */
+    silc_client_replace_from_channels(client, conn, client_entry,
+                                     client_entry2);
 
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+    /* Notify application */
+    client->internal->ops->notify(client, conn, type,
+                                 client_entry, client_entry2);
 
-    idp = silc_id_payload_parse(tmp, tmp_len);
-    if (!idp)
-      goto out;
+    /* Free old client entry */
+    silc_client_del_client_entry(client, conn, client_entry);
 
-    /* Find Client entry */
-    if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
-      client_id = silc_id_payload_parse_id(tmp, tmp_len);
-      if (!client_id) {
-       silc_id_payload_free(idp);
+    break;
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    {
+      /*
+       * Someone changed a channel mode
+       */
+      char *passphrase, *cipher, *hmac;
+      SilcPublicKey founder_key = NULL;
+      SilcBufferStruct chpks;
+
+      SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
+
+      /* Get channel entry */
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 SILC_ID_CHANNEL);
+      if (!channel_id)
+       goto out;
+      channel = silc_client_get_channel_by_id(client, conn, channel_id);
+      if (!channel)
        goto out;
-      }
 
-      client_entry = silc_client_get_client_by_id(client, conn, client_id);
-      if (!client_entry) {
-       silc_id_payload_free(idp);
+      /* Get ID */
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!tmp)
        goto out;
-      }
-    } else {
-      server_id = silc_id_payload_parse_id(tmp, tmp_len);
-      if (!server_id) {
-       silc_id_payload_free(idp);
+      id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+      if (!id)
        goto out;
+
+      /* Find Client entry */
+      if (id_type == SILC_ID_CLIENT) {
+       /* Find Client entry */
+       client_id = id;
+       client_entry = silc_client_get_client_by_id(client, conn, client_id);
+       if (!client_entry) {
+         silc_client_channel_set_wait(client, conn, channel,
+                                      conn->cmd_ident + 1);
+         silc_client_notify_by_server_resolve(client, conn, packet,
+                                              SILC_ID_CLIENT, client_id);
+         goto out;
+       }
+
+       if (!client_entry->nickname) {
+         if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+           /* Attach to existing resolving */
+           SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+           res->packet = silc_packet_context_dup(packet);
+           res->context = client;
+           res->sock = silc_socket_dup(conn->sock);
+           silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                       client_entry->resolve_cmd_ident,
+                                       silc_client_notify_by_server_pending,
+                                       res);
+           goto out;
+         }
+
+         /* Do new resolving */
+         silc_client_channel_set_wait(client, conn, channel,
+                                      conn->cmd_ident + 1);
+         silc_client_notify_by_server_resolve(client, conn, packet,
+                                              SILC_ID_CLIENT, client_id);
+         client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+         client_entry->resolve_cmd_ident = conn->cmd_ident;
+         goto out;
+       }
+      } else if (id_type == SILC_ID_SERVER) {
+       /* Find Server entry */
+       server_id = id;
+       server = silc_client_get_server_by_id(client, conn, server_id);
+       if (!server) {
+         silc_client_channel_set_wait(client, conn, channel,
+                                      conn->cmd_ident + 1);
+         silc_client_notify_by_server_resolve(client, conn, packet,
+                                              SILC_ID_SERVER, server_id);
+         server = silc_client_add_server(client, conn, NULL, NULL, server_id);
+         if (!server)
+           goto out;
+
+         server->resolve_cmd_ident = conn->cmd_ident;
+         server_id = NULL;
+         goto out;
+       }
+
+       /* If entry being resoled, wait for it before processing this notify */
+       if (server->resolve_cmd_ident) {
+         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+         res->packet = silc_packet_context_dup(packet);
+         res->context = client;
+         res->sock = silc_socket_dup(conn->sock);
+         silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                     server->resolve_cmd_ident,
+                                     silc_client_notify_by_server_pending,
+                                     res);
+         goto out;
+       }
+
+       /* Save the pointer to the client_entry pointer */
+       client_entry = (SilcClientEntry)server;
+      } else {
+       /* Find Channel entry */
+       silc_free(channel_id);
+       channel_id = id;
+       client_entry = (SilcClientEntry)
+         silc_client_get_channel_by_id(client, conn, channel_id);
+       if (!client_entry) {
+         silc_client_channel_set_wait(client, conn, channel,
+                                      conn->cmd_ident + 1);
+         silc_client_notify_by_server_resolve(client, conn, packet,
+                                              SILC_ID_CHANNEL, channel_id);
+         goto out;
+       }
       }
-      
-      server = silc_client_get_server_by_id(client, conn, server_id);
-      if (!server) {
-       silc_id_payload_free(idp);
-       silc_free(server_id);
+
+      /* Get the mode */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (!tmp)
        goto out;
-      }
-      
-      /* Save the pointer to the client_entry pointer */
-      client_entry = (SilcClientEntry)server;
-      silc_free(server_id);
-    }
 
-    /* Get the mode */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp) {
-      silc_id_payload_free(idp);
-      goto out;
-    }
+      SILC_GET32_MSB(mode, tmp);
 
-    SILC_GET32_MSB(mode, tmp);
+      /* If information is being resolved for this channel, wait for it */
+      if (channel->resolve_cmd_ident) {
+       silc_client_channel_wait(client, conn, channel, packet);
+       goto out;
+      }
 
-    /* Get channel entry */
-    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
-                               SILC_ID_CHANNEL);
-    if (!channel_id) {
-      silc_id_payload_free(idp);
-      goto out;
-    }
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel) {
-      silc_id_payload_free(idp);
-      goto out;
-    }
+      /* Save the new mode */
+      channel->mode = mode;
 
-    /* Save the new mode */
-    channel->mode = mode;
+      /* Get the cipher */
+      cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
 
-    /* Get the hmac */
-    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
-    if (tmp) {
-      unsigned char hash[32];
+      /* Get the hmac */
+      hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
+      if (hmac) {
+       unsigned char hash[SILC_HASH_MAXLEN];
 
-      if (channel->hmac)
-       silc_hmac_free(channel->hmac);
-      if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
-       goto out;
+       if (channel->hmac)
+         silc_hmac_free(channel->hmac);
+       if (!silc_hmac_alloc(hmac, NULL, &channel->hmac))
+         goto out;
 
-      silc_hash_make(silc_hmac_get_hash(channel->hmac), 
-                    channel->key, channel->key_len / 8,
-                    hash);
-      silc_hmac_set_key(channel->hmac, hash, 
-                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
-      memset(hash, 0, sizeof(hash));
-    }
+       silc_hash_make(silc_hmac_get_hash(channel->hmac),
+                      channel->key, channel->key_len / 8,
+                      hash);
+       silc_hmac_set_key(channel->hmac, hash,
+                         silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+       memset(hash, 0, sizeof(hash));
+      }
 
-    /* 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->internal->ops->notify(client, conn, type, 
-                                 silc_id_payload_get_type(idp), 
-                                 client_entry, mode, NULL, tmp, channel);
+      /* Get the passphrase if it was set */
+      passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
+
+      /* Get the channel founder key if it was set */
+      tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
+      if (tmp) {
+       if (!silc_pkcs_public_key_payload_decode(tmp, tmp_len, &founder_key))
+         founder_key = NULL;
+      }
 
-    silc_id_payload_free(idp);
+      /* Get user limit */
+      tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
+      if (tmp && tmp_len == 4)
+        SILC_GET32_MSB(channel->user_limit, tmp);
+      if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
+        channel->user_limit = 0;
+
+      /* Get the channel public key that was added or removed */
+      tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
+      if (tmp)
+       silc_buffer_set(&chpks, tmp, 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->internal->ops->notify(client, conn, type, id_type,
+                                   client_entry, mode, cipher, hmac,
+                                   passphrase, founder_key,
+                                   tmp ? &chpks : NULL, channel);
+
+      if (founder_key)
+       silc_pkcs_public_key_free(founder_key);
+    }
     break;
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
@@ -596,20 +883,104 @@ void silc_client_notify_by_server(SilcClient client,
 
     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
 
-    /* Get Client ID */
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Get 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)
+    id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+    if (!id)
       goto out;
 
     /* Find Client entry */
-    client_entry = silc_client_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 (id_type == SILC_ID_CLIENT) {
+      /* Find Client entry */
+      client_id = id;
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry) {
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CLIENT, client_id);
+       goto out;
+      }
+
+      if (!client_entry->nickname) {
+       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+         /* Attach to existing resolving */
+         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+         res->packet = silc_packet_context_dup(packet);
+         res->context = client;
+         res->sock = silc_socket_dup(conn->sock);
+         silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                     client_entry->resolve_cmd_ident,
+                                     silc_client_notify_by_server_pending,
+                                     res);
+         goto out;
+       }
+
+       /* Do new resolving */
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CLIENT, client_id);
+        client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+        client_entry->resolve_cmd_ident = conn->cmd_ident;
+       goto out;
+      }
+    } else if (id_type == SILC_ID_SERVER) {
+      /* Find Server entry */
+      server_id = id;
+      server = silc_client_get_server_by_id(client, conn, server_id);
+      if (!server) {
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_SERVER, server_id);
+       server = silc_client_add_server(client, conn, NULL, NULL, server_id);
+       if (!server)
+         goto out;
+
+       server->resolve_cmd_ident = conn->cmd_ident;
+       server_id = NULL;
+       goto out;
+      }
+
+      /* If entry being resoled, wait for it before processing this notify */
+      if (server->resolve_cmd_ident) {
+       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+       res->packet = silc_packet_context_dup(packet);
+       res->context = client;
+       res->sock = silc_socket_dup(conn->sock);
+       silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                   server->resolve_cmd_ident,
+                                   silc_client_notify_by_server_pending, res);
+       goto out;
+      }
+
+      /* Save the pointer to the client_entry pointer */
+      client_entry = (SilcClientEntry)server;
+    } else {
+      /* Find Channel entry */
+      silc_free(channel_id);
+      channel_id = id;
+      client_entry = (SilcClientEntry)
+       silc_client_get_channel_by_id(client, conn, channel_id);
+      if (!client_entry) {
+       silc_client_channel_set_wait(client, conn, channel,
+                                    conn->cmd_ident + 1);
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CHANNEL, channel_id);
+       goto out;
+      }
     }
 
     /* Get the mode */
@@ -619,30 +990,30 @@ void silc_client_notify_by_server(SilcClient client,
 
     SILC_GET32_MSB(mode, tmp);
 
+    /* If information is being resolved for this channel, wait for it */
+    if (channel->resolve_cmd_ident) {
+      silc_client_channel_wait(client, conn, channel, packet);
+      goto out;
+    }
+
     /* 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);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Find target Client entry */
-    client_entry2 = 
+    client_entry2 =
       silc_client_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)
+    if (!client_entry2) {
+      silc_client_notify_by_server_resolve(client, conn, packet,
+                                          SILC_ID_CLIENT, client_id);
       goto out;
-    channel = silc_client_get_channel_by_id(client, conn, channel_id);
-    if (!channel)
-      break;
+    }
 
     /* Save the mode */
     chu = silc_client_on_channel(channel, client_entry2);
@@ -652,8 +1023,8 @@ void silc_client_notify_by_server(SilcClient client,
     /* 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->internal->ops->notify(client, conn, type, 
-                                 client_entry, mode, 
+    client->internal->ops->notify(client, conn, type,
+                                 id_type, client_entry, mode,
                                  client_entry2, channel);
     break;
 
@@ -668,7 +1039,7 @@ void silc_client_notify_by_server(SilcClient client,
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    
+
     /* Notify application */
     client->internal->ops->notify(client, conn, type, tmp);
     break;
@@ -685,7 +1056,7 @@ void silc_client_notify_by_server(SilcClient client,
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
 
@@ -695,17 +1066,19 @@ void silc_client_notify_by_server(SilcClient client,
       goto out;
 
     silc_free(channel_id);
+    channel_id = NULL;
 
     /* 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);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
 
     /* Replace the Channel ID */
-    silc_client_replace_channel_id(client, conn, channel, channel_id);
+    if (silc_client_replace_channel_id(client, conn, channel, channel_id))
+      channel_id = NULL;
 
     /* Notify application */
     client->internal->ops->notify(client, conn, type, channel, channel);
@@ -723,7 +1096,7 @@ void silc_client_notify_by_server(SilcClient client,
     if (!tmp)
       goto out;
 
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
@@ -741,80 +1114,170 @@ void silc_client_notify_by_server(SilcClient client,
     if (!channel)
       break;
 
-    /* Get the kicker */
+    /* From protocol version 1.1 we get the kicker's client ID as well */
     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (!tmp)
-      goto out;
-
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
-    if (!client_id)
-      goto out;
+    if (tmp) {
+      silc_free(client_id);
+      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+      if (!client_id)
+       goto out;
 
-    /* Find kicker's client entry and if not found resolve it */
-    client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry2) {
-      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
-      goto out;
-    } else {
-      if (client_entry2 != conn->local_entry)
-       silc_client_nickname_format(client, conn, client_entry2);
+      /* Find kicker's client entry and if not found resolve it */
+      client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry2) {
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CLIENT, client_id);
+       goto out;
+      } else {
+       if (client_entry2 != conn->local_entry)
+         silc_client_nickname_format(client, conn, client_entry2);
+      }
     }
 
     /* Get comment */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 
+    /* Remove kicked client from channel */
+    if (client_entry != conn->local_entry) {
+      chu = silc_client_on_channel(channel, client_entry);
+      if (chu) {
+       silc_hash_table_del(client_entry->channels, channel);
+       silc_hash_table_del(channel->user_list, client_entry);
+       silc_free(chu);
+      }
+    }
+
     /* 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->internal->ops->notify(client, conn, type, client_entry, tmp, 
+    client->internal->ops->notify(client, conn, type, client_entry, tmp,
                                  client_entry2, channel);
 
-    /* If I was kicked from channel, remove the channel */
+    /* Remove kicked client (us) from channel */
     if (client_entry == conn->local_entry) {
+      /* If I was kicked from channel, remove the channel */
       if (conn->current_channel == channel)
        conn->current_channel = NULL;
-      silc_idcache_del_by_id(conn->channel_cache, channel->id);
-      silc_free(channel->channel_name);
-      silc_free(channel->id);
-      silc_free(channel->key);
-      silc_cipher_free(channel->channel_key);
-      silc_free(channel);
+      silc_client_del_channel(client, conn, channel);
+    } else {
+      if (!silc_hash_table_count(client_entry->channels)) {
+       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+       res->context = client;
+       res->sock = silc_socket_dup(conn->sock);
+       res->packet = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
+       silc_schedule_task_add(client->schedule, conn->sock->sock,
+                              silc_client_notify_check_client, res,
+                              (5 + (silc_rng_get_rn16(client->rng) % 529)),
+                              0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      }
     }
     break;
 
   case SILC_NOTIFY_TYPE_KILLED:
-    /*
-     * A client (maybe me) was killed from the network.
-     */
+    {
+      /*
+       * A client (maybe me) was killed from the network.
+       */
+      char *comment;
+      SilcUInt32 comment_len;
 
-    SILC_LOG_DEBUG(("Notify: KILLED"));
+      SILC_LOG_DEBUG(("Notify: KILLED"));
 
-    /* Get Client ID */
-    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
-    if (!tmp)
-      goto out;
+      /* 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;
+      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+      if (!client_id)
+       goto out;
 
-    /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, client_id);
-    if (!client_entry)
-      goto out;
+      /* Find Client entry */
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry)
+       goto out;
 
-    /* Get comment */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      /* Get comment */
+      comment = silc_argument_get_arg_type(args, 2, &comment_len);
+
+      /* From protocol version 1.1 we get killer's client ID as well */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (tmp) {
+       silc_free(client_id);
+       client_id = NULL;
+       id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+       if (!id)
+         goto out;
+
+       /* Find Client entry */
+       if (id_type == SILC_ID_CLIENT) {
+         /* Find Client entry */
+         client_id = id;
+         client_entry2 = silc_client_get_client_by_id(client, conn,
+                                                      client_id);
+         if (!client_entry) {
+           silc_client_notify_by_server_resolve(client, conn, packet,
+                                                SILC_ID_CLIENT, client_id);
+           goto out;
+         }
+       } else if (id_type == SILC_ID_SERVER) {
+         /* Find Server entry */
+         server_id = id;
+         server = silc_client_get_server_by_id(client, conn, server_id);
+         if (!server) {
+           silc_client_notify_by_server_resolve(client, conn, packet,
+                                                SILC_ID_SERVER, server_id);
+           server = silc_client_add_server(client, conn, NULL, NULL,
+                                           server_id);
+           if (!server)
+             goto out;
+
+           server->resolve_cmd_ident = conn->cmd_ident;
+           server_id = NULL;
+           goto out;
+         }
 
-    /* Notify application. */
-    client->internal->ops->notify(client, conn, type, client_entry, tmp);
+         if (server->resolve_cmd_ident) {
+           SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+           res->packet = silc_packet_context_dup(packet);
+           res->context = client;
+           res->sock = silc_socket_dup(conn->sock);
+           silc_client_command_pending(conn, SILC_COMMAND_NONE,
+                                       server->resolve_cmd_ident,
+                                       silc_client_notify_by_server_pending,
+                                       res);
+           goto out;
+         }
 
-    if (client_entry != conn->local_entry)
-      /* Remove the client from all channels and free it */
-      silc_client_del_client(client, conn, client_entry);
+         /* Save the pointer to the client_entry pointer */
+         client_entry2 = (SilcClientEntry)server;
+       } else {
+         /* Find Channel entry */
+         channel_id = id;
+         channel = silc_client_get_channel_by_id(client, conn, channel_id);
+         if (!channel) {
+           silc_client_notify_by_server_resolve(client, conn, packet,
+                                                SILC_ID_CHANNEL, channel_id);
+           goto out;
+         }
 
+         /* Save the pointer to the client_entry pointer */
+         client_entry2 = (SilcClientEntry)channel;
+         silc_free(channel_id);
+         channel_id = NULL;
+       }
+      }
+
+      /* Notify application. */
+      client->internal->ops->notify(client, conn, type, client_entry,
+                                   comment, id_type, client_entry2);
+
+      if (client_entry != conn->local_entry)
+       /* Remove the client from all channels and free it */
+       silc_client_del_client(client, conn, client_entry);
+    }
     break;
-    
+
   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
     {
       /*
@@ -822,7 +1285,7 @@ void silc_client_notify_by_server(SilcClient client,
        * from channels as they quit as well.
        */
       SilcClientEntry *clients = NULL;
-      uint32 clients_count = 0;
+      SilcUInt32 clients_count = 0;
       int i;
 
       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
@@ -831,14 +1294,14 @@ void silc_client_notify_by_server(SilcClient client,
        /* Get Client ID */
        tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
        if (tmp) {
-         client_id = silc_id_payload_parse_id(tmp, tmp_len);
+         client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
          if (!client_id)
            goto out;
-         
+
          /* Get the client entry */
          client_entry = silc_client_get_client_by_id(client, conn, client_id);
          if (client_entry) {
-           clients = silc_realloc(clients, sizeof(*clients) * 
+           clients = silc_realloc(clients, sizeof(*clients) *
                                   (clients_count + 1));
            clients[clients_count] = client_entry;
            clients_count++;
@@ -851,7 +1314,7 @@ void silc_client_notify_by_server(SilcClient client,
       /* Notify application. We don't keep server entries so the server
         entry is returned as NULL. The client's are returned as array
         of SilcClientEntry pointers. */
-      client->internal->ops->notify(client, conn, type, NULL, 
+      client->internal->ops->notify(client, conn, type, NULL,
                                    clients, clients_count);
 
       for (i = 0; i < clients_count; i++) {
@@ -868,6 +1331,140 @@ void silc_client_notify_by_server(SilcClient client,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_ERROR:
+    {
+      /*
+       * Some has occurred and server is notifying us about it.
+       */
+      SilcStatus error;
+
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!tmp && tmp_len != 1)
+       goto out;
+      error = (SilcStatus)tmp[0];
+
+      SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
+
+      if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+       if (tmp) {
+         client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+         if (!client_id)
+           goto out;
+         client_entry = silc_client_get_client_by_id(client, conn,
+                                                     client_id);
+         if (client_entry)
+           silc_client_del_client(client, conn, client_entry);
+       }
+      }
+
+      /* Notify application. */
+      client->internal->ops->notify(client, conn, type, error);
+    }
+    break;
+
+  case SILC_NOTIFY_TYPE_WATCH:
+    {
+      /*
+       * Received notify about some client we are watching
+       */
+      SilcNotifyType notify = 0;
+      SilcBool del_client = FALSE;
+      unsigned char *pk;
+      SilcUInt32 pk_len;
+      SilcPublicKey public_key = NULL;
+
+      SILC_LOG_DEBUG(("Notify: WATCH"));
+
+      /* Get sender 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, NULL);
+      if (!client_id)
+       goto out;
+
+      /* Find Client entry and if not found query it */
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry) {
+       silc_client_notify_by_server_resolve(client, conn, packet,
+                                            SILC_ID_CLIENT, client_id);
+       goto out;
+      }
+
+      /* Get user mode */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (!tmp || tmp_len != 4)
+       goto out;
+      SILC_GET32_MSB(mode, tmp);
+
+      /* Get notify type */
+      tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+      if (tmp && tmp_len != 2)
+       goto out;
+      if (tmp)
+       SILC_GET16_MSB(notify, tmp);
+
+      /* Get nickname */
+      tmp = silc_argument_get_arg_type(args, 2, NULL);
+      if (tmp) {
+       char *tmp_nick = NULL;
+
+       if (client->internal->params->nickname_parse)
+         client->internal->params->nickname_parse(client_entry->nickname,
+                                                  &tmp_nick);
+       else
+         tmp_nick = strdup(tmp);
+
+       /* If same nick, the client was new to us and has become "present"
+          to network.  Send NULL as nick to application. */
+       if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
+         tmp = NULL;
+
+       silc_free(tmp_nick);
+      }
+
+      /* Get public key, if present */
+      pk = silc_argument_get_arg_type(args, 5, &pk_len);
+      if (pk && !client_entry->public_key) {
+        if (silc_pkcs_public_key_payload_decode(pk, pk_len, &public_key)) {
+         client_entry->public_key = public_key;
+         public_key = NULL;
+       }
+      }
+
+      /* Notify application. */
+      client->internal->ops->notify(client, conn, type, client_entry,
+                                   tmp, mode, notify,
+                                   client_entry->public_key);
+
+      client_entry->mode = mode;
+
+      /* If nickname was changed, remove the client entry unless the
+        client is on some channel */
+      if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
+         !silc_hash_table_count(client_entry->channels))
+       del_client = TRUE;
+      else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
+              notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
+              notify == SILC_NOTIFY_TYPE_KILLED)
+       del_client = TRUE;
+
+      if (del_client) {
+       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+       res->context = client;
+       res->sock = silc_socket_dup(conn->sock);
+       res->packet = client_id;
+        client_id = NULL;
+       silc_schedule_task_add(client->schedule, conn->sock->sock,
+                              silc_client_notify_del_client_cb, res,
+                              1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      }
+
+      silc_pkcs_public_key_free(public_key);
+    }
+    break;
+
   default:
     break;
   }
@@ -876,4 +1473,5 @@ void silc_client_notify_by_server(SilcClient client,
   silc_notify_payload_free(payload);
   silc_free(client_id);
   silc_free(channel_id);
+  silc_free(server_id);
 }