Merge commit 'origin/silc.1.1.branch'
[silc.git] / lib / silcclient / client_notify.c
index 9087ba35f22c8f754887ea8f5a7b78b5719dccad..2ccb574c1ac8a384d7fd6a36939e8a0c5b097e52 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2006 Pekka Riikonen
+  Copyright (C) 1997 - 2008 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
 
   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
 
 /* Notify processing context */
 typedef struct {
 
 /* Notify processing context */
 typedef struct {
-  SilcPacket packet;
-  SilcNotifyPayload payload;
-  SilcFSMThread fsm;
-  SilcChannelEntry channel;
+  SilcPacket packet;                 /* Notify packet */
+  SilcNotifyPayload payload;         /* Parsed notify payload */
+  SilcFSMThread fsm;                 /* Notify FSM thread */
+  SilcChannelEntry channel;          /* Channel entry being resolved */
+  SilcClientEntry client_entry;              /* Client entry being resolved */
+  SilcUInt32 resolve_retry;          /* Resolving retry counter */
 } *SilcClientNotify;
 
 /************************ Static utility functions **************************/
 
 } *SilcClientNotify;
 
 /************************ Static utility functions **************************/
 
-/* Entry resolving callback.  This will continue processing the notify. */
+/* The client entires in notify processing are resolved if they do not exist,
+   or they are not valid.  We go to this callback after resolving where we
+   check if the client entry has become valid.  If resolving succeeded the
+   entry is valid but remains invalid if resolving failed.  This callback
+   will continue processing the notify.  We use this callback also with other
+   entry resolving. */
 
 static void silc_client_notify_resolved(SilcClient client,
                                        SilcClientConnection conn,
 
 static void silc_client_notify_resolved(SilcClient client,
                                        SilcClientConnection conn,
@@ -46,8 +53,21 @@ static void silc_client_notify_resolved(SilcClient client,
 {
   SilcClientNotify notify = context;
 
 {
   SilcClientNotify notify = context;
 
-  /* If no entries found, just finish the notify processing, a silent error */
-  if (!entries)
+  /* If entry is still invalid, resolving failed.  Finish notify processing. */
+  if (notify->client_entry && !notify->client_entry->internal.valid) {
+    /* If resolving timedout try it again many times. */
+    if (status != SILC_STATUS_ERR_TIMEDOUT || ++notify->resolve_retry > 1000) {
+      silc_fsm_next(notify->fsm, silc_client_notify_processed);
+
+      /* Unref client only in case of non-timeout error.  In case of timeout
+        occurred, the routine reprocessing the notify is expected not to
+        create new references of the entry. */
+      silc_client_unref_client(client, conn, notify->client_entry);
+    }
+  }
+
+  /* If no entries found, just finish the notify processing */
+  if (!entries && !notify->client_entry)
     silc_fsm_next(notify->fsm, silc_client_notify_processed);
 
   if (notify->channel) {
     silc_fsm_next(notify->fsm, silc_client_notify_processed);
 
   if (notify->channel) {
@@ -75,7 +95,7 @@ static SilcBool silc_client_notify_wait_continue(SilcClient client,
   /* Continue after last command reply received */
   if (SILC_STATUS_IS_ERROR(status) || status == SILC_STATUS_OK ||
       status == SILC_STATUS_LIST_END)
   /* Continue after last command reply received */
   if (SILC_STATUS_IS_ERROR(status) || status == SILC_STATUS_OK ||
       status == SILC_STATUS_LIST_END)
-    SILC_FSM_CALL_CONTINUE(notify->fsm);
+    SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
 
   return TRUE;
 }
 
   return TRUE;
 }
@@ -99,7 +119,7 @@ SILC_FSM_STATE(silc_client_notify)
   }
 
   if (!silc_notify_get_args(payload)) {
   }
 
   if (!silc_notify_get_args(payload)) {
-    SILC_LOG_DEBUG(("Malformed notify"));
+    SILC_LOG_DEBUG(("Malformed notify %d", silc_notify_get_type(payload)));
     silc_notify_payload_free(payload);
     silc_packet_free(packet);
     return SILC_FSM_FINISH;
     silc_notify_payload_free(payload);
     silc_packet_free(packet);
     return SILC_FSM_FINISH;
@@ -112,7 +132,6 @@ SILC_FSM_STATE(silc_client_notify)
     return SILC_FSM_FINISH;
   }
 
     return SILC_FSM_FINISH;
   }
 
-  /* Save notify payload to packet context during processing */
   notify->packet = packet;
   notify->payload = payload;
   notify->fsm = fsm;
   notify->packet = packet;
   notify->payload = payload;
   notify->fsm = fsm;
@@ -210,7 +229,7 @@ SILC_FSM_STATE(silc_client_notify)
     break;
   }
 
     break;
   }
 
-  return SILC_FSM_YIELD;
+  return SILC_FSM_CONTINUE;
 }
 
 /* Notify processed, finish the packet processing thread */
 }
 
 /* Notify processed, finish the packet processing thread */
@@ -238,6 +257,8 @@ SILC_FSM_STATE(silc_client_notify_none)
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
 
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
 
+  SILC_LOG_DEBUG(("Notify: NONE"));
+
   /* Notify application */
   NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
 
   /* Notify application */
   NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
 
@@ -277,6 +298,14 @@ SILC_FSM_STATE(silc_client_notify_invite)
 
   /* Get the channel entry */
   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
 
   /* Get the channel entry */
   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel) {
+    /** Resolve channel */
+    SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+                                         client, conn, &id.u.channel_id,
+                                         silc_client_notify_resolved,
+                                         notify));
+    /* NOT REACHED */
+  }
 
   /* If channel is being resolved handle notify after resolving */
   if (channel->internal.resolve_cmd_ident) {
 
   /* If channel is being resolved handle notify after resolving */
   if (channel->internal.resolve_cmd_ident) {
@@ -285,7 +314,7 @@ SILC_FSM_STATE(silc_client_notify_invite)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -295,7 +324,7 @@ SILC_FSM_STATE(silc_client_notify_invite)
 
   /* Find Client entry and if not found query it */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
 
   /* Find Client entry and if not found query it */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0]) {
+  if (!client_entry || !client_entry->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     notify->channel = channel;
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     notify->channel = channel;
@@ -303,7 +332,7 @@ SILC_FSM_STATE(silc_client_notify_invite)
                  silc_client_get_client_by_id_resolve(
                                         client, conn, &id.u.client_id, NULL,
                                         silc_client_notify_resolved,
                  silc_client_get_client_by_id_resolve(
                                         client, conn, &id.u.client_id, NULL,
                                         silc_client_notify_resolved,
-                                        fsm));
+                                        notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -353,7 +382,7 @@ SILC_FSM_STATE(silc_client_notify_join)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -361,27 +390,40 @@ SILC_FSM_STATE(silc_client_notify_join)
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  /* Find Client entry and if not found query it */
-  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0] ||
+  /* Find client entry and if not found query it.  If we just queried it
+     don't do it again, unless some data (like username) is missing. */
+  client_entry = notify->client_entry;
+  if (!client_entry)
+    client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+  if (!client_entry || !client_entry->internal.valid ||
       !client_entry->username[0]) {
     /** Resolve client */
       !client_entry->username[0]) {
     /** Resolve client */
-    silc_client_unref_client(client, conn, client_entry);
     notify->channel = channel;
     notify->channel = channel;
+    notify->client_entry = client_entry;
     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
                  silc_client_get_client_by_id_resolve(
     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
                  silc_client_get_client_by_id_resolve(
-                                        client, conn, &id.u.client_id, NULL,
-                                        silc_client_notify_resolved,
-                                        fsm));
+                                        client, conn, client_entry ?
+                                        &client_entry->id : &id.u.client_id,
+                                        NULL, silc_client_notify_resolved,
+                                        notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+  silc_rwlock_wrlock(channel->internal.lock);
+
   if (client_entry != conn->local_entry)
   if (client_entry != conn->local_entry)
-    silc_client_nickname_format(client, conn, client_entry);
+    silc_client_nickname_format(client, conn, client_entry, FALSE);
 
   /* Join the client to channel */
 
   /* Join the client to channel */
-  if (!silc_client_add_to_channel(channel, client_entry, 0))
+  if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0)) {
+    silc_rwlock_unlock(channel->internal.lock);
+    silc_rwlock_unlock(client_entry->internal.lock);
     goto out;
     goto out;
+  }
+
+  silc_rwlock_unlock(channel->internal.lock);
+  silc_rwlock_unlock(client_entry->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -410,7 +452,6 @@ SILC_FSM_STATE(silc_client_notify_leave)
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
   SilcChannelEntry channel = NULL;
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
   SilcChannelEntry channel = NULL;
-  SilcChannelUser chu;
   SilcID id;
 
   SILC_LOG_DEBUG(("Notify: LEAVE"));
   SilcID id;
 
   SILC_LOG_DEBUG(("Notify: LEAVE"));
@@ -430,7 +471,7 @@ SILC_FSM_STATE(silc_client_notify_leave)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -444,30 +485,8 @@ SILC_FSM_STATE(silc_client_notify_leave)
     goto out;
 
   /* Remove client from channel */
     goto out;
 
   /* Remove client from channel */
-  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);
-  }
-
-#if 0 /* Kind of useless, server will return error if client keeps using
-        non-existing client, and the entry is removed then. */
-  /* 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);
-  }
-#endif
+  if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+    goto out;
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -491,10 +510,12 @@ SILC_FSM_STATE(silc_client_notify_signoff)
   SilcClient client = conn->client;
   SilcClientNotify notify = state_context;
   SilcNotifyPayload payload = notify->payload;
   SilcClient client = conn->client;
   SilcClientNotify notify = state_context;
   SilcNotifyPayload payload = notify->payload;
+  SilcPacket packet = notify->packet;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
-   unsigned char *tmp;
+  SilcChannelEntry channel = NULL;
+  unsigned char *tmp;
   SilcUInt32 tmp_len;
   SilcID id;
 
   SilcUInt32 tmp_len;
   SilcID id;
 
@@ -511,23 +532,28 @@ SILC_FSM_STATE(silc_client_notify_signoff)
 
   /* Get signoff message */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
 
   /* Get signoff message */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-  if (tmp_len > 128)
+  if (tmp && tmp_len > 128)
     tmp[128] = '\0';
 
     tmp[128] = '\0';
 
-  /* Notify application */
-  NOTIFY(client, conn, type, client_entry, tmp);
+  if (packet->dst_id_type == SILC_ID_CHANNEL)
+    if (silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                      &id.u.channel_id, sizeof(id.u.channel_id)))
+      channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
 
 
-  /* Remove from all channels */
-  silc_client_remove_from_channels(client, conn, client_entry);
+  /* Notify application */
+  if (client_entry->internal.valid)
+    NOTIFY(client, conn, type, client_entry, tmp, channel);
 
 
-#if 0
-  /* Remove from cache */
-  silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
-#endif
+  /* Remove from channel */
+  if (channel) {
+    silc_client_remove_from_channel(client, conn, channel, client_entry);
+    silc_client_unref_channel(client, conn, channel);
+  }
 
 
-  /* Free data */
+  /* Delete client */
+  client_entry->internal.valid = FALSE;
+  silc_client_del_client(client, conn, client_entry);
   silc_client_unref_client(client, conn, client_entry);
   silc_client_unref_client(client, conn, client_entry);
-  silc_client_del_client_entry(client, conn, client_entry);
 
  out:
   /** Notify processed */
 
  out:
   /** Notify processed */
@@ -573,11 +599,11 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
-  /* Get ID */
+  /* Get ID of topic changer */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
@@ -588,18 +614,26 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->nickname[0]) {
-      /** Resolve client */
-      silc_client_unref_client(client, conn, client_entry);
-      notify->channel = channel;
-      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
-                   silc_client_get_client_by_id_resolve(
+    client_entry = notify->client_entry;
+    if (!client_entry) {
+      client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+      if (!client_entry || !client_entry->internal.valid) {
+       /** Resolve client */
+       notify->channel = channel;
+       notify->client_entry = client_entry;
+       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                     silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
-                                          fsm));
-      /* NOT REACHED */
+                                          notify));
+       /* NOT REACHED */
+      }
     }
     }
+
+    /* If client is not on channel, ignore this notify */
+    if (!silc_client_on_channel(channel, client_entry))
+      goto out;
+
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
@@ -611,7 +645,7 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
                    silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
                    silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
       /* NOT REACHED */
     }
     entry = server;
@@ -626,14 +660,16 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
                    silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
                    silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
   }
 
       /* NOT REACHED */
     }
     entry = channel_entry;
   }
 
+  silc_rwlock_wrlock(channel->internal.lock);
   silc_free(channel->topic);
   channel->topic = silc_memdup(tmp, strlen(tmp));
   silc_free(channel->topic);
   channel->topic = silc_memdup(tmp, strlen(tmp));
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
@@ -665,9 +701,10 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
-  unsigned char *tmp, *nick, oldnick[128 + 1];
+  unsigned char *tmp, oldnick[256 + 1];
   SilcUInt32 tmp_len;
   SilcUInt32 tmp_len;
-  SilcID id;
+  SilcID id, id2;
+  SilcBool valid;
 
   SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
 
 
   SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
 
@@ -680,58 +717,58 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
       SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
     goto out;
 
       SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
     goto out;
 
-  /* Find old Client entry */
-  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0]) {
-    /** Resolve client */
-    silc_client_unref_client(client, conn, client_entry);
-    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
-                                        client, conn, &id.u.client_id, NULL,
-                                        silc_client_notify_resolved,
-                                        fsm));
-    /* NOT REACHED */
-  }
-
   /* Get new Client ID */
   /* Get new Client ID */
-  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
+  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id2, NULL))
+    goto out;
+
+  /* Ignore my ID */
+  if (conn->local_id &&
+      SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
     goto out;
 
     goto out;
 
+  /* Find old client entry.  If we don't have the entry, we ignore this
+     notify. */
+  client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
+  if (!client_entry)
+    goto out;
+  valid = client_entry->internal.valid;
+
   /* Take the new nickname */
   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
   if (!tmp)
     goto out;
 
   /* Take the new nickname */
   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
   if (!tmp)
     goto out;
 
+  silc_rwlock_wrlock(client_entry->internal.lock);
+
   /* Check whether nickname changed at all.  It is possible that nick
      change notify is received but nickname didn't change, only the
      ID changes.  If Client ID hash match, nickname didn't change. */
   /* Check whether nickname changed at all.  It is possible that nick
      change notify is received but nickname didn't change, only the
      ID changes.  If Client ID hash match, nickname didn't change. */
-  if (SILC_ID_COMPARE_HASH(&client_entry->id, &id.u.client_id) &&
+  if (SILC_ID_COMPARE_HASH(&client_entry->id, &id2.u.client_id) &&
       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
     /* Nickname didn't change.  Update only Client ID.  We don't notify
        application because nickname didn't change. */
       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
     /* Nickname didn't change.  Update only Client ID.  We don't notify
        application because nickname didn't change. */
-    silc_idcache_update(conn->internal->client_cache, client_entry,
-                       &client_entry->id, &id.u.client_id, NULL, NULL, FALSE);
+    silc_mutex_lock(conn->internal->lock);
+    silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
+                                  &id2.u.client_id, NULL, FALSE);
+    silc_mutex_unlock(conn->internal->lock);
+    silc_rwlock_unlock(client_entry->internal.lock);
     goto out;
   }
 
     goto out;
   }
 
-  /* Normalize nickname */
-  nick = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, 128, NULL);
-  if (!nick)
-    goto out;
-
-  /* Update nickname */
-  if (!silc_idcache_update(conn->internal->client_cache, client_entry,
-                          NULL, NULL, client_entry->nickname_normalized,
-                          nick, TRUE)) {
-    silc_free(nick);
+  /* Change the nickname */
+  memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
+  if (!silc_client_change_nickname(client, conn, client_entry, tmp,
+                                  &id2.u.client_id, NULL, 0)) {
+    silc_rwlock_unlock(client_entry->internal.lock);
     goto out;
   }
     goto out;
   }
-  memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
-  memcpy(client_entry->nickname, tmp, tmp_len);
-  client_entry->nickname_normalized = nick;
-  silc_client_nickname_format(client, conn, client_entry);
 
 
-  /* Notify application */
-  NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
+  silc_rwlock_unlock(client_entry->internal.lock);
+
+  /* Notify application, if client entry is valid.  We do not send nick change
+     notify for entries that were invalid (application doesn't know them). */
+  if (valid)
+    NOTIFY(client, conn, type, client_entry, oldnick, client_entry->nickname);
 
  out:
   /** Notify processed */
 
  out:
   /** Notify processed */
@@ -781,7 +818,7 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -791,24 +828,32 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     goto out;
   SILC_GET32_MSB(mode, tmp);
 
     goto out;
   SILC_GET32_MSB(mode, tmp);
 
-  /* Get ID */
+  /* Get ID of who changed the mode */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->nickname[0]) {
-      /** Resolve client */
-      silc_client_unref_client(client, conn, client_entry);
-      notify->channel = channel;
-      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
-                   silc_client_get_client_by_id_resolve(
+    client_entry = notify->client_entry;
+    if (!client_entry) {
+      client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+      if (!client_entry || !client_entry->internal.valid) {
+       /** Resolve client */
+       notify->channel = channel;
+       notify->client_entry = client_entry;
+       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                     silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
-                                          fsm));
-      /* NOT REACHED */
+                                          notify));
+       /* NOT REACHED */
+      }
     }
     }
+
+    /* If client is not on channel, ignore this notify */
+    if (!silc_client_on_channel(channel, client_entry))
+      goto out;
+
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
@@ -820,7 +865,7 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
                    silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
                    silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
       /* NOT REACHED */
     }
     entry = server;
@@ -835,17 +880,21 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
                    silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
                    silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
   }
 
       /* NOT REACHED */
     }
     entry = channel_entry;
   }
 
+  silc_rwlock_wrlock(channel->internal.lock);
+
   /* Get the channel founder key if it was set */
   tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
   if (tmp) {
   /* Get the channel founder key if it was set */
   tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
   if (tmp) {
-    if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key))
+    if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
       goto out;
+    }
     if (!channel->founder_key) {
       channel->founder_key = founder_key;
       founder_key = NULL;
     if (!channel->founder_key) {
       channel->founder_key = founder_key;
       founder_key = NULL;
@@ -861,8 +910,10 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     unsigned char hash[SILC_HASH_MAXLEN];
     SilcHmac newhmac;
 
     unsigned char hash[SILC_HASH_MAXLEN];
     SilcHmac newhmac;
 
-    if (!silc_hmac_alloc(hmac, NULL, &newhmac))
+    if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+      silc_rwlock_unlock(channel->internal.lock);
       goto out;
       goto out;
+    }
 
     /* Get HMAC key from the old HMAC context, and update it to the new one */
     tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
 
     /* Get HMAC key from the old HMAC context, and update it to the new one */
     tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
@@ -887,14 +938,17 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
     channel->user_limit = 0;
 
   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
     channel->user_limit = 0;
 
-  /* Save the new mode */
-  channel->mode = mode;
-
   /* Get the channel public key that was added or removed */
   tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
   if (tmp)
   /* Get the channel public key that was added or removed */
   tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
   if (tmp)
-    chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
-                                            SILC_ARGUMENT_PUBLIC_KEY);
+    silc_client_channel_save_public_keys(channel, tmp, tmp_len, FALSE);
+  else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
+    silc_client_channel_save_public_keys(channel, NULL, 0, TRUE);
+
+  /* Save the new mode */
+  channel->mode = mode;
+
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
@@ -957,34 +1011,62 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
+    /* NOT REACHED */
+  }
+
+  /* Get target Client ID */
+  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
+    goto out;
+
+  /* Find target Client entry */
+  client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
+  if (!client_entry2 || !client_entry2->internal.valid) {
+    /** Resolve client */
+    silc_client_unref_client(client, conn, client_entry2);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &id2.u.client_id, NULL,
+                                        silc_client_notify_resolved,
+                                        notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
+  /* If target client is not on channel, ignore this notify */
+  if (!silc_client_on_channel(channel, client_entry2))
+    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 the mode */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
   if (!tmp)
     goto out;
   SILC_GET32_MSB(mode, tmp);
 
-  /* Get ID */
+  /* Get ID of mode changer */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (id.type == SILC_ID_CLIENT) {
     /* Find Client entry */
-    client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (!client_entry || !client_entry->nickname[0]) {
-      /** Resolve client */
-      silc_client_unref_client(client, conn, client_entry);
-      notify->channel = channel;
-      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
-                   silc_client_get_client_by_id_resolve(
+    client_entry = notify->client_entry;
+    if (!client_entry) {
+      client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+      if (!client_entry || !client_entry->internal.valid) {
+       /** Resolve client */
+       notify->channel = channel;
+       notify->client_entry = client_entry;
+       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                     silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
-                                          fsm));
-      /* NOT REACHED */
+                                          notify));
+       /* NOT REACHED */
+      }
     }
     }
+
+    /* If client is not on channel, ignore this notify */
+    if (!silc_client_on_channel(channel, client_entry))
+      goto out;
+
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
     entry = client_entry;
   } else if (id.type == SILC_ID_SERVER) {
     /* Find Server entry */
@@ -996,7 +1078,7 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
                    silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
                    silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
       /* NOT REACHED */
     }
     entry = server;
@@ -1011,33 +1093,18 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
                    silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
                    silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
   }
 
       /* NOT REACHED */
     }
     entry = channel_entry;
   }
 
-  /* Get target Client ID */
-  if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
-    goto out;
-
-  /* Find target Client entry */
-  client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
-  if (!client_entry2 || !client_entry2->nickname[0]) {
-    /** Resolve client */
-    silc_client_unref_client(client, conn, client_entry);
-    silc_client_unref_client(client, conn, client_entry2);
-    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
-                                        client, conn, &id2.u.client_id, NULL,
-                                        silc_client_notify_resolved,
-                                        fsm));
-    /* NOT REACHED */
-  }
-
   /* Save the mode */
   /* Save the mode */
+  silc_rwlock_wrlock(channel->internal.lock);
   chu = silc_client_on_channel(channel, client_entry2);
   if (chu)
     chu->mode = mode;
   chu = silc_client_on_channel(channel, client_entry2);
   if (chu)
     chu->mode = mode;
+  silc_rwlock_unlock(channel->internal.lock);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
 
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
@@ -1121,7 +1188,7 @@ SILC_FSM_STATE(silc_client_notify_channel_change)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -1158,7 +1225,6 @@ SILC_FSM_STATE(silc_client_notify_kicked)
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry, client_entry2;
   SilcChannelEntry channel = NULL;
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry, client_entry2;
   SilcChannelEntry channel = NULL;
-  SilcChannelUser chu;
   unsigned char *tmp;
   SilcUInt32 tmp_len;
   SilcID id;
   unsigned char *tmp;
   SilcUInt32 tmp_len;
   SilcID id;
@@ -1180,15 +1246,15 @@ SILC_FSM_STATE(silc_client_notify_kicked)
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
                                      conn, SILC_COMMAND_NONE,
                                      channel->internal.resolve_cmd_ident,
                                      silc_client_notify_wait_continue,
-                                     fsm));
+                                     notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
-  /* Get Client ID */
+  /* Get the kicked Client ID */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  /* Find Client entry */
+  /* Find client entry */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
   if (!client_entry)
     goto out;
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
   if (!client_entry)
     goto out;
@@ -1199,7 +1265,7 @@ SILC_FSM_STATE(silc_client_notify_kicked)
 
   /* Find kicker's client entry and if not found resolve it */
   client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
 
   /* Find kicker's client entry and if not found resolve it */
   client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry2 || !client_entry2->nickname[0]) {
+  if (!client_entry2 || !client_entry2->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     silc_client_unref_client(client, conn, client_entry2);
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     silc_client_unref_client(client, conn, client_entry2);
@@ -1208,7 +1274,7 @@ SILC_FSM_STATE(silc_client_notify_kicked)
                  silc_client_get_client_by_id_resolve(
                                         client, conn, &id.u.client_id, NULL,
                                         silc_client_notify_resolved,
                  silc_client_get_client_by_id_resolve(
                                         client, conn, &id.u.client_id, NULL,
                                         silc_client_notify_resolved,
-                                        fsm));
+                                        notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -1217,12 +1283,8 @@ SILC_FSM_STATE(silc_client_notify_kicked)
 
   /* Remove kicked client from channel */
   if (client_entry != conn->local_entry) {
 
   /* 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);
-    }
+    if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+      goto out;
   }
 
   /* Notify application. */
   }
 
   /* Notify application. */
@@ -1232,6 +1294,7 @@ SILC_FSM_STATE(silc_client_notify_kicked)
   if (client_entry == conn->local_entry) {
     if (conn->current_channel == channel)
       conn->current_channel = NULL;
   if (client_entry == conn->local_entry) {
     if (conn->current_channel == channel)
       conn->current_channel = NULL;
+    silc_client_empty_channel(client, conn, channel);
     silc_client_del_channel(client, conn, channel);
   }
 
     silc_client_del_channel(client, conn, channel);
   }
 
@@ -1287,14 +1350,14 @@ SILC_FSM_STATE(silc_client_notify_killed)
     /* Find Client entry */
     client_entry2 = silc_client_get_client_by_id(client, conn,
                                                 &id.u.client_id);
     /* Find Client entry */
     client_entry2 = silc_client_get_client_by_id(client, conn,
                                                 &id.u.client_id);
-    if (!client_entry2 || !client_entry2->nickname[0]) {
+    if (!client_entry2 || !client_entry2->internal.valid) {
       /** Resolve client */
       silc_client_unref_client(client, conn, client_entry);
       silc_client_unref_client(client, conn, client_entry2);
       SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
       /** Resolve client */
       silc_client_unref_client(client, conn, client_entry);
       silc_client_unref_client(client, conn, client_entry2);
       SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
                                           client, conn, &id.u.client_id, NULL,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = client_entry2;
       /* NOT REACHED */
     }
     entry = client_entry2;
@@ -1306,7 +1369,7 @@ SILC_FSM_STATE(silc_client_notify_killed)
       SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
       SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
       /* NOT REACHED */
     }
     entry = server;
@@ -1319,7 +1382,7 @@ SILC_FSM_STATE(silc_client_notify_killed)
       SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
       SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
       /* NOT REACHED */
     }
     entry = channel_entry;
@@ -1329,8 +1392,11 @@ SILC_FSM_STATE(silc_client_notify_killed)
   NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
 
   /* Delete the killed client */
   NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
 
   /* Delete the killed client */
-  if (client_entry != conn->local_entry)
+  if (client_entry != conn->local_entry) {
+    silc_client_remove_from_channels(client, conn, client_entry);
+    client_entry->internal.valid = FALSE;
     silc_client_del_client(client, conn, client_entry);
     silc_client_del_client(client, conn, client_entry);
+  }
 
  out:
   silc_client_unref_client(client, conn, client_entry);
 
  out:
   silc_client_unref_client(client, conn, client_entry);
@@ -1359,16 +1425,24 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
+  SilcServerEntry server_entry = NULL;
   SilcDList clients;
   SilcID id;
   int i;
 
   SilcDList clients;
   SilcID id;
   int i;
 
-  SILC_LOG_DEBUG(("Notify: SIGNOFF"));
+  SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
 
   clients = silc_dlist_init();
   if (!clients)
     goto out;
 
 
   clients = silc_dlist_init();
   if (!clients)
     goto out;
 
+  /* Get server ID */
+  if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
+    goto out;
+
+  /* Get server, in case we have it cached */
+  server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
+
   for (i = 1; i < silc_argument_get_arg_num(args); i++) {
     /* Get Client ID */
     if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
   for (i = 1; i < silc_argument_get_arg_num(args); i++) {
     /* Get Client ID */
     if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
@@ -1376,21 +1450,24 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
 
     /* Get the client entry */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
 
     /* Get the client entry */
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (client_entry)
+    if (client_entry && client_entry->internal.valid)
       silc_dlist_add(clients, client_entry);
   }
 
       silc_dlist_add(clients, client_entry);
   }
 
-  /* Notify application.  We don't keep server entries so the server
-     entry is returned as NULL. The client's are returned as list. */
-  NOTIFY(client, conn, type, NULL, clients);
+  /* Notify application. */
+  NOTIFY(client, conn, type, server_entry, clients);
 
   /* Delete the clients */
   silc_dlist_start(clients);
 
   /* Delete the clients */
   silc_dlist_start(clients);
-  while ((client_entry = silc_dlist_get(clients)))
+  while ((client_entry = silc_dlist_get(clients))) {
+    silc_client_remove_from_channels(client, conn, client_entry);
+    client_entry->internal.valid = FALSE;
     silc_client_del_client(client, conn, client_entry);
     silc_client_del_client(client, conn, client_entry);
+  }
 
  out:
   /** Notify processed */
 
  out:
   /** Notify processed */
+  silc_client_unref_server(client, conn, server_entry);
   silc_client_list_free(client, conn, clients);
   silc_fsm_next(fsm, silc_client_notify_processed);
   return SILC_FSM_CONTINUE;
   silc_client_list_free(client, conn, clients);
   silc_fsm_next(fsm, silc_client_notify_processed);
   return SILC_FSM_CONTINUE;
@@ -1427,7 +1504,8 @@ SILC_FSM_STATE(silc_client_notify_error)
     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
       goto out;
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
       goto out;
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-    if (client_entry) {
+    if (client_entry && client_entry != conn->local_entry) {
+      silc_client_remove_from_channels(client, conn, client_entry);
       silc_client_del_client(client, conn, client_entry);
       silc_client_unref_client(client, conn, client_entry);
     }
       silc_client_del_client(client, conn, client_entry);
       silc_client_unref_client(client, conn, client_entry);
     }
@@ -1456,7 +1534,6 @@ SILC_FSM_STATE(silc_client_notify_watch)
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
   SilcNotifyType ntype = 0;
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
   SilcNotifyType ntype = 0;
-  SilcBool del_client = FALSE;
   unsigned char *pk, *tmp;
   SilcUInt32 mode, pk_len, tmp_len;
   SilcPublicKey public_key = NULL;
   unsigned char *pk, *tmp;
   SilcUInt32 mode, pk_len, tmp_len;
   SilcPublicKey public_key = NULL;
@@ -1468,15 +1545,15 @@ SILC_FSM_STATE(silc_client_notify_watch)
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
 
-  /* Find Client entry and if not found resolve it */
+  /* Find client entry and if not found resolve it */
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
-  if (!client_entry || !client_entry->nickname[0]) {
+  if (!client_entry || !client_entry->internal.valid) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
                                         client, conn, &id.u.client_id, NULL,
                                         silc_client_notify_resolved,
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
                                         client, conn, &id.u.client_id, NULL,
                                         silc_client_notify_resolved,
-                                        fsm));
+                                        notify));
     /* NOT REACHED */
   }
 
     /* NOT REACHED */
   }
 
@@ -1498,11 +1575,8 @@ SILC_FSM_STATE(silc_client_notify_watch)
   if (tmp) {
     char *tmp_nick = 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);
+    silc_client_nickname_parse(client, conn, client_entry->nickname,
+                              &tmp_nick);
 
     /* If same nick, the client was new to us and has become "present"
        to network.  Send NULL as nick to application. */
 
     /* If same nick, the client was new to us and has become "present"
        to network.  Send NULL as nick to application. */
@@ -1527,19 +1601,14 @@ SILC_FSM_STATE(silc_client_notify_watch)
 
   client_entry->mode = mode;
 
 
   client_entry->mode = mode;
 
-  /* If nickname was changed, remove the client entry unless the
-     client is on some channel */
-  /* XXX, why do we need to remove the client entry?? */
-  if (tmp && ntype == SILC_NOTIFY_TYPE_NICK_CHANGE &&
-      !silc_hash_table_count(client_entry->channels))
-    del_client = TRUE;
-  else if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
-          ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
-          ntype == SILC_NOTIFY_TYPE_KILLED)
-    del_client = TRUE;
-
-  if (del_client)
+  /* Remove client that left the network. */
+  if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
+      ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
+      ntype == SILC_NOTIFY_TYPE_KILLED) {
+    silc_client_remove_from_channels(client, conn, client_entry);
+    client_entry->internal.valid = FALSE;
     silc_client_del_client(client, conn, client_entry);
     silc_client_del_client(client, conn, client_entry);
+  }
 
   if (public_key)
     silc_pkcs_public_key_free(public_key);
 
   if (public_key)
     silc_pkcs_public_key_free(public_key);