SILC_FSM_* macro API changes.
[silc.git] / lib / silcclient / client_notify.c
index d54e6ccc811e20919f1f9e5fda96fce1b64d3c79..740e3e5942e89a92bf9bedec93ce4df60b682231 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2006 Pekka Riikonen
+  Copyright (C) 1997 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 #define NOTIFY conn->client->internal->ops->notify
 
+/* Notify processing context */
+typedef struct {
+  SilcPacket packet;
+  SilcNotifyPayload payload;
+  SilcFSMThread fsm;
+  SilcChannelEntry channel;
+} *SilcClientNotify;
+
 /************************ Static utility functions **************************/
 
 /* Entry resolving callback.  This will continue processing the notify. */
@@ -36,12 +44,40 @@ static void silc_client_notify_resolved(SilcClient client,
                                        SilcDList entries,
                                        void *context)
 {
+  SilcClientNotify notify = context;
+
   /* If no entries found, just finish the notify processing, a silent error */
   if (!entries)
-    silc_fsm_next(context, silc_client_notify_processed);
+    silc_fsm_next(notify->fsm, silc_client_notify_processed);
+
+  if (notify->channel) {
+    notify->channel->internal.resolve_cmd_ident = 0;
+    silc_client_unref_channel(client, conn, notify->channel);
+  }
 
   /* Continue processing the notify */
-  SILC_FSM_CALL_CONTINUE_SYNC(context);
+  SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
+}
+
+/* Continue notify processing after it was suspended while waiting for
+   channel information being resolved. */
+
+static SilcBool silc_client_notify_wait_continue(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcCommand command,
+                                                SilcStatus status,
+                                                SilcStatus error,
+                                                void *context,
+                                                va_list ap)
+{
+  SilcClientNotify notify = context;
+
+  /* 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_SYNC(notify->fsm);
+
+  return TRUE;
 }
 
 /********************************* Notify ***********************************/
@@ -51,6 +87,7 @@ static void silc_client_notify_resolved(SilcClient client,
 SILC_FSM_STATE(silc_client_notify)
 {
   SilcPacket packet = state_context;
+  SilcClientNotify notify;
   SilcNotifyPayload payload;
 
   payload = silc_notify_payload_parse(silc_buffer_data(&packet->buffer),
@@ -62,14 +99,23 @@ SILC_FSM_STATE(silc_client_notify)
   }
 
   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;
+  }
+
+  notify = silc_calloc(1, sizeof(*notify));
+  if (!notify) {
     silc_notify_payload_free(payload);
     silc_packet_free(packet);
     return SILC_FSM_FINISH;
   }
 
-  /* Save notify payload to packet context during processing */
-  packet->next = (void *)payload;
+  notify->packet = packet;
+  notify->payload = payload;
+  notify->fsm = fsm;
+  silc_fsm_set_state_context(fsm, notify);
 
   /* Process the notify */
   switch (silc_notify_get_type(payload)) {
@@ -158,6 +204,7 @@ SILC_FSM_STATE(silc_client_notify)
     /** Unknown notify */
     silc_notify_payload_free(payload);
     silc_packet_free(packet);
+    silc_free(notify);
     return SILC_FSM_FINISH;
     break;
   }
@@ -169,10 +216,13 @@ SILC_FSM_STATE(silc_client_notify)
 
 SILC_FSM_STATE(silc_client_notify_processed)
 {
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcPacket packet = notify->packet;
+  SilcNotifyPayload payload = notify->payload;
+
   silc_notify_payload_free(payload);
   silc_packet_free(packet);
+  silc_free(notify);
   return SILC_FSM_FINISH;
 }
 
@@ -182,11 +232,13 @@ SILC_FSM_STATE(silc_client_notify_none)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->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));
 
@@ -203,8 +255,8 @@ SILC_FSM_STATE(silc_client_notify_invite)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
@@ -226,6 +278,19 @@ SILC_FSM_STATE(silc_client_notify_invite)
 
   /* Get the channel entry */
   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
+
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
 
   /* Get sender Client ID */
   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
@@ -236,10 +301,12 @@ SILC_FSM_STATE(silc_client_notify_invite)
   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(
+    notify->channel = channel;
+    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));
+                                        notify));
     /* NOT REACHED */
   }
 
@@ -263,8 +330,8 @@ SILC_FSM_STATE(silc_client_notify_join)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
@@ -282,6 +349,17 @@ SILC_FSM_STATE(silc_client_notify_join)
   if (!channel)
     goto out;
 
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
+
   /* Get Client ID */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
@@ -292,18 +370,21 @@ SILC_FSM_STATE(silc_client_notify_join)
       !client_entry->username[0]) {
     /** Resolve client */
     silc_client_unref_client(client, conn, client_entry);
-    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+    notify->channel = channel;
+    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));
+                                        notify));
     /* NOT REACHED */
   }
 
   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 */
-  silc_client_add_to_channel(channel, client_entry, 0);
+  if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0))
+    goto out;
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -325,13 +406,13 @@ SILC_FSM_STATE(silc_client_notify_leave)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  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 = NULL;
   SilcChannelEntry channel = NULL;
-  SilcChannelUser chu;
   SilcID id;
 
   SILC_LOG_DEBUG(("Notify: LEAVE"));
@@ -344,6 +425,17 @@ SILC_FSM_STATE(silc_client_notify_leave)
   if (!channel)
     goto out;
 
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
+
   /* Get Client ID */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
@@ -354,30 +446,7 @@ SILC_FSM_STATE(silc_client_notify_leave)
     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
+  silc_client_remove_from_channel(client, conn, channel, client_entry);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, channel);
@@ -399,12 +468,14 @@ SILC_FSM_STATE(silc_client_notify_signoff)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  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;
-   unsigned char *tmp;
+  SilcChannelEntry channel;
+  unsigned char *tmp;
   SilcUInt32 tmp_len;
   SilcID id;
 
@@ -421,23 +492,27 @@ SILC_FSM_STATE(silc_client_notify_signoff)
 
   /* 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';
 
   /* Notify application */
   NOTIFY(client, conn, type, client_entry, tmp);
 
-  /* Remove from all channels */
-  silc_client_remove_from_channels(client, conn, client_entry);
-
-#if 0
-  /* Remove from cache */
-  silc_idcache_del_by_context(conn->internal->client_cache, client_entry);
-#endif
+  /* Remove from channel */
+  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);
+      if (channel) {
+       silc_client_remove_from_channel(client, conn, channel, client_entry);
+       silc_client_unref_channel(client, conn, channel);
+      }
+    }
+  }
 
-  /* Free data */
+  /* Delete client */
+  silc_client_del_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 */
@@ -453,8 +528,9 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  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 = NULL;
@@ -475,6 +551,17 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
   if (!channel)
     goto out;
 
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
+
   /* Get ID */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
@@ -490,10 +577,12 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
     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(
+      notify->channel = channel;
+      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));
+                                          notify));
       /* NOT REACHED */
     }
     entry = client_entry;
@@ -502,10 +591,12 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
     if (!server) {
       /** Resolve server */
-      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+      notify->channel = channel;
+      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                   silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
@@ -515,10 +606,12 @@ SILC_FSM_STATE(silc_client_notify_topic_set)
                                                  &id.u.channel_id);
     if (!channel_entry) {
       /** Resolve channel */
-      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+      notify->channel = channel;
+      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                   silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
@@ -552,14 +645,14 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   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[128 + 1];
   SilcUInt32 tmp_len;
-  SilcID id;
+  SilcID id, id2;
 
   SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
 
@@ -572,6 +665,15 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
       SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
     goto out;
 
+  /* Get new Client ID */
+  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;
+
   /* 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]) {
@@ -580,14 +682,10 @@ SILC_FSM_STATE(silc_client_notify_nick_change)
     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 */
   }
 
-  /* Get new Client ID */
-  if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
-    goto out;
-
   /* Take the new nickname */
   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
   if (!tmp)
@@ -596,31 +694,22 @@ SILC_FSM_STATE(silc_client_notify_nick_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_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);
     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);
-    goto out;
-  }
+  /* Change the nickname */
   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);
+  if (!silc_client_change_nickname(client, conn, client_entry, tmp,
+                                  &id2.u.client_id, NULL, 0))
+    goto out;
 
   /* Notify application */
   NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
@@ -640,8 +729,9 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  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 = NULL;
@@ -665,6 +755,17 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
   if (!channel)
     goto out;
 
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
+
   /* Get the mode */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
   if (!tmp)
@@ -681,10 +782,12 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     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(
+      notify->channel = channel;
+      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));
+                                          notify));
       /* NOT REACHED */
     }
     entry = client_entry;
@@ -693,10 +796,12 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
     if (!server) {
       /** Resolve server */
-      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+      notify->channel = channel;
+      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                   silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
@@ -706,10 +811,12 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
                                                  &id.u.channel_id);
     if (!channel_entry) {
       /** Resolve channel */
-      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+      notify->channel = channel;
+      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                   silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
@@ -770,6 +877,8 @@ SILC_FSM_STATE(silc_client_notify_cmode_change)
     chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
                                             SILC_ARGUMENT_PUBLIC_KEY);
 
+  /* XXX add to/remove from channel pubkeys channel->channel_pubkeys */
+
   /* Notify application. */
   NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
         passphrase, channel->founder_key, chpks, channel);
@@ -800,8 +909,9 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  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 = NULL, client_entry2 = NULL;
@@ -823,6 +933,33 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
   if (!channel)
     goto out;
 
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     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->nickname[0]) {
+    /** 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 */
+  }
+
   /* Get the mode */
   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
   if (!tmp)
@@ -839,10 +976,12 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
     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(
+      notify->channel = channel;
+      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));
+                                          notify));
       /* NOT REACHED */
     }
     entry = client_entry;
@@ -851,10 +990,12 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
     if (!server) {
       /** Resolve server */
-      SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
+      notify->channel = channel;
+      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                   silc_client_get_server_by_id_resolve(
                                           client, conn, &id.u.server_id,
                                           silc_client_notify_resolved,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
@@ -864,32 +1005,17 @@ SILC_FSM_STATE(silc_client_notify_cumode_change)
                                                  &id.u.channel_id);
     if (!channel_entry) {
       /** Resolve channel */
-      SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
+      notify->channel = channel;
+      SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+                   silc_client_get_channel_by_id_resolve(
                                    client, conn, &id.u.channel_id,
                                    silc_client_notify_resolved,
-                                   fsm));
+                                   notify));
       /* 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 */
   chu = silc_client_on_channel(channel, client_entry2);
   if (chu)
@@ -921,8 +1047,8 @@ SILC_FSM_STATE(silc_client_notify_motd)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   unsigned char *tmp;
@@ -952,8 +1078,8 @@ SILC_FSM_STATE(silc_client_notify_channel_change)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcChannelEntry channel = NULL;
@@ -970,6 +1096,17 @@ SILC_FSM_STATE(silc_client_notify_channel_change)
   if (!channel)
     goto out;
 
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
+
   /* Get the new ID */
   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
@@ -996,19 +1133,38 @@ SILC_FSM_STATE(silc_client_notify_kicked)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  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, client_entry2;
   SilcChannelEntry channel = NULL;
-  SilcChannelUser chu;
   unsigned char *tmp;
   SilcUInt32 tmp_len;
   SilcID id;
 
   SILC_LOG_DEBUG(("Notify: KICKED"));
 
+  /* Get channel entry */
+  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
+                     &id.u.channel_id, sizeof(id.u.channel_id)))
+    goto out;
+  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
+  if (!channel)
+    goto out;
+
+  /* If channel is being resolved handle notify after resolving */
+  if (channel->internal.resolve_cmd_ident) {
+    silc_client_unref_channel(client, conn, channel);
+    SILC_FSM_CALL(silc_client_command_pending(
+                                     conn, SILC_COMMAND_NONE,
+                                     channel->internal.resolve_cmd_ident,
+                                     silc_client_notify_wait_continue,
+                                     notify));
+    /* NOT REACHED */
+  }
+
   /* Get Client ID */
   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
@@ -1018,14 +1174,6 @@ SILC_FSM_STATE(silc_client_notify_kicked)
   if (!client_entry)
     goto out;
 
-  /* Get channel entry */
-  if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
-                     &id.u.channel_id, sizeof(id.u.channel_id)))
-    goto out;
-  channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
-  if (!channel)
-    goto out;
-
   /* Get kicker's Client ID */
   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
     goto out;
@@ -1036,10 +1184,12 @@ SILC_FSM_STATE(silc_client_notify_kicked)
     /** 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(
+    notify->channel = channel;
+    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));
+                                        notify));
     /* NOT REACHED */
   }
 
@@ -1047,14 +1197,8 @@ SILC_FSM_STATE(silc_client_notify_kicked)
   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);
-    }
-  }
+  if (client_entry != conn->local_entry)
+    silc_client_remove_from_channel(client, conn, channel, client_entry);
 
   /* Notify application. */
   NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
@@ -1063,6 +1207,7 @@ SILC_FSM_STATE(silc_client_notify_kicked)
   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);
   }
 
@@ -1084,8 +1229,8 @@ SILC_FSM_STATE(silc_client_notify_killed)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL, client_entry2 = NULL;
@@ -1125,7 +1270,7 @@ SILC_FSM_STATE(silc_client_notify_killed)
       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;
@@ -1137,7 +1282,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,
-                                          fsm));
+                                          notify));
       /* NOT REACHED */
     }
     entry = server;
@@ -1150,7 +1295,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,
-                                   fsm));
+                                   notify));
       /* NOT REACHED */
     }
     entry = channel_entry;
@@ -1160,8 +1305,10 @@ SILC_FSM_STATE(silc_client_notify_killed)
   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);
     silc_client_del_client(client, conn, client_entry);
+  }
 
  out:
   silc_client_unref_client(client, conn, client_entry);
@@ -1185,8 +1332,8 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
@@ -1194,7 +1341,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
   SilcID id;
   int i;
 
-  SILC_LOG_DEBUG(("Notify: SIGNOFF"));
+  SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
 
   clients = silc_dlist_init();
   if (!clients)
@@ -1217,8 +1364,10 @@ SILC_FSM_STATE(silc_client_notify_server_signoff)
 
   /* 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);
     silc_client_del_client(client, conn, client_entry);
+  }
 
  out:
   /** Notify processed */
@@ -1235,8 +1384,8 @@ SILC_FSM_STATE(silc_client_notify_error)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry;
@@ -1259,6 +1408,7 @@ SILC_FSM_STATE(silc_client_notify_error)
       goto out;
     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
     if (client_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);
     }
@@ -1281,12 +1431,12 @@ SILC_FSM_STATE(silc_client_notify_watch)
 {
   SilcClientConnection conn = fsm_context;
   SilcClient client = conn->client;
-  SilcPacket packet = state_context;
-  SilcNotifyPayload payload = (SilcNotifyPayload)packet->next;
+  SilcClientNotify notify = state_context;
+  SilcNotifyPayload payload = notify->payload;
   SilcNotifyType type = silc_notify_get_type(payload);
   SilcArgumentPayload args = silc_notify_get_args(payload);
   SilcClientEntry client_entry = NULL;
-  SilcNotifyType notify = 0;
+  SilcNotifyType ntype = 0;
   SilcBool del_client = FALSE;
   unsigned char *pk, *tmp;
   SilcUInt32 mode, pk_len, tmp_len;
@@ -1307,7 +1457,7 @@ SILC_FSM_STATE(silc_client_notify_watch)
     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 */
   }
 
@@ -1322,18 +1472,15 @@ SILC_FSM_STATE(silc_client_notify_watch)
   if (tmp && tmp_len != 2)
     goto out;
   if (tmp)
-    SILC_GET16_MSB(notify, tmp);
+    SILC_GET16_MSB(ntype, 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);
+    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. */
@@ -1353,7 +1500,7 @@ SILC_FSM_STATE(silc_client_notify_watch)
   }
 
   /* Notify application. */
-  NOTIFY(client, conn, type, client_entry, tmp, mode, notify,
+  NOTIFY(client, conn, type, client_entry, tmp, mode, ntype,
         client_entry->public_key);
 
   client_entry->mode = mode;
@@ -1361,16 +1508,18 @@ SILC_FSM_STATE(silc_client_notify_watch)
   /* 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 && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
+  if (tmp && ntype == 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)
+  else if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
+          ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
+          ntype == SILC_NOTIFY_TYPE_KILLED)
     del_client = TRUE;
 
-  if (del_client)
+  if (del_client) {
+    silc_client_remove_from_channels(client, conn, client_entry);
     silc_client_del_client(client, conn, client_entry);
+  }
 
   if (public_key)
     silc_pkcs_public_key_free(public_key);