Fixed topic annoucning, server signoff handling, added -D option,
[silc.git] / apps / silcd / packet_receive.c
index 29752416068c1b32778e492cfe1fc2561a041ca0..ac06a7d2e4c6c0f0c5d4eb74c503febc4d1c0509 100644 (file)
@@ -86,7 +86,7 @@ void silc_server_notify(SilcServer server,
      we will broadcast it. The sending socket really cannot be router or
      the router is buggy. If this packet is coming from router then it must
      have the broadcast flag set already and we won't do anything. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
+  if (server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
@@ -97,12 +97,13 @@ void silc_server_notify(SilcServer server,
       if (!channel_id)
        goto out;
 
-      silc_server_packet_send_dest(server, server->router->connection, 
-                                  packet->type,
-                                  packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                                  channel_id, SILC_ID_CHANNEL,
-                                  packet->buffer->data, packet->buffer->len, 
-                                  FALSE);
+      if (!server->standalone)
+       silc_server_packet_send_dest(server, server->router->connection, 
+                                    packet->type, packet->flags | 
+                                    SILC_PACKET_FLAG_BROADCAST, 
+                                    channel_id, SILC_ID_CHANNEL,
+                                    packet->buffer->data, 
+                                    packet->buffer->len, FALSE);
       silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, 
                                   packet->type, packet->flags,
                                   channel_id, SILC_ID_CHANNEL,
@@ -110,11 +111,12 @@ void silc_server_notify(SilcServer server,
                                   FALSE, TRUE);
     } else {
       /* Packet is destined to client or server */
-      silc_server_packet_send(server, server->router->connection, 
-                             packet->type,
-                             packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                             packet->buffer->data, packet->buffer->len, 
-                             FALSE);
+      if (!server->standalone)
+       silc_server_packet_send(server, server->router->connection, 
+                               packet->type,
+                               packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                               packet->buffer->data, packet->buffer->len, 
+                               FALSE);
       silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
                              packet->type, packet->flags,
                              packet->buffer->data, packet->buffer->len, 
@@ -229,6 +231,14 @@ void silc_server_notify(SilcServer server,
     silc_hash_table_add(client->channels, channel, chl);
     silc_free(client_id);
     channel->user_count++;
+    channel->disabled = FALSE;
+
+    /* Update statistics */
+    if (server->server_type == SILC_ROUTER) {
+      if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+       server->stat.cell_chanclients++;
+      server->stat.chanclients++;
+    }
 
     break;
 
@@ -338,6 +348,11 @@ void silc_server_notify(SilcServer server,
     /* Remove the client from all channels. */
     silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_SIGNOFF);
+
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
     break;
@@ -353,22 +368,24 @@ void silc_server_notify(SilcServer server,
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
     if (!client_id)
       goto out;
 
     /* Get client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, TRUE, &cache);
-    if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
+    if (id_type == SILC_ID_CLIENT) {
+      client = silc_idlist_find_client_by_id(server->global_list, 
                                             client_id, TRUE, &cache);
       if (!client) {
-       silc_free(client_id);
-       goto out;
+       client = silc_idlist_find_client_by_id(server->local_list, 
+                                              client_id, TRUE, &cache);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
       }
+      silc_free(client_id);
     }
-    silc_free(client_id);
 
     /* Get the topic */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
@@ -399,13 +416,16 @@ void silc_server_notify(SilcServer server,
     if (channel->topic && !strcmp(channel->topic, tmp))
       goto out;
 
-    /* Get user's channel entry and check that topic set is allowed. */
-    if (!silc_server_client_on_channel(client, channel, &chl))
-      goto out;
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-      SILC_LOG_DEBUG(("Topic change is not allowed"));
-      goto out;
+    if (client) {
+      /* Get user's channel entry and check that topic set is allowed. */
+      if (!silc_server_client_on_channel(client, channel, &chl))
+       goto out;
+      if (channel->mode & SILC_CHANNEL_MODE_TOPIC &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+       SILC_LOG_DEBUG(("Topic change is not allowed"));
+       goto out;
+      }
     }
 
     /* Change the topic */
@@ -455,16 +475,18 @@ void silc_server_notify(SilcServer server,
       nickname = silc_argument_get_arg_type(args, 3, &nickname_len);;
 
       /* Replace the Client ID */
-      client = silc_idlist_replace_client_id(server->global_list, client_id,
+      client = silc_idlist_replace_client_id(server,
+                                            server->global_list, client_id,
                                             client_id2, nickname);
       if (!client)
-       client = silc_idlist_replace_client_id(server->local_list, client_id, 
+       client = silc_idlist_replace_client_id(server,
+                                              server->local_list, client_id, 
                                               client_id2, nickname);
 
       if (client) {
        /* Send the NICK_CHANGE notify type to local clients on the channels
           this client is joined to. */
-       silc_server_send_notify_on_channels(server, NULL, client,
+       silc_server_send_notify_on_channels(server, client, client,
                                            SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
                                            id, tmp_len, id2, tmp_len,
                                            nickname, nickname ?
@@ -545,6 +567,20 @@ void silc_server_notify(SilcServer server,
        SILC_LOG_DEBUG(("CMODE change is not allowed"));
        goto out;
       }
+    } else {
+      if (server->server_type == SILC_ROUTER &&
+         channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH &&
+         !(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
+       SILC_LOG_DEBUG(("Enforcing sender to change channel mode"));
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     channel->mode, server->id,
+                                     SILC_ID_SERVER,
+                                     channel->cipher,
+                                     channel->hmac_name,
+                                     channel->passphrase,
+                                     channel->founder_key);
+       goto out;
+      }
     }
 
     /* Send the same notify to the channel */
@@ -567,9 +603,6 @@ void silc_server_notify(SilcServer server,
                                   FALSE : !server->standalone);
     }
 
-    /* Change mode */
-    channel->mode = mode;
-
     /* Get the hmac */
     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
     if (tmp) {
@@ -583,8 +616,7 @@ void silc_server_notify(SilcServer server,
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
       silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
-                    channel->key_len / 8, 
-                    hash);
+                    channel->key_len / 8, hash);
       silc_hmac_set_key(channel->hmac, hash, 
                        silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
@@ -597,6 +629,59 @@ void silc_server_notify(SilcServer server,
       channel->passphrase = silc_memdup(tmp, tmp_len);
     }
 
+    /* Get founder public key */
+    tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
+    if (tmp && mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+      if (channel->founder_key)
+       silc_pkcs_public_key_free(channel->founder_key);
+      channel->founder_key = NULL;
+      silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+
+      if (!channel->founder_key || 
+         (client && client->data.public_key && 
+          server->server_type == SILC_ROUTER &&
+          !silc_pkcs_public_key_compare(channel->founder_key,
+                                        client->data.public_key))) {
+       /* A really buggy server isn't checking public keys correctly.
+          It's not possible that the mode setter and founder wouldn't
+          have same public key. */
+       SILC_LOG_DEBUG(("Enforcing sender to change channel mode"));
+
+       mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     mode, server->id, SILC_ID_SERVER,
+                                     channel->cipher, 
+                                     channel->hmac_name,
+                                     channel->passphrase, NULL);
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+      } else if (client && !client->data.public_key) {
+       client->data.public_key = 
+         silc_pkcs_public_key_copy(channel->founder_key);
+      }
+    }
+
+    if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH && !channel->founder_key &&
+       server->server_type == SILC_ROUTER) {
+      SILC_LOG_DEBUG(("Enforcing sender to change channel mode"));
+      mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
+      silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                   mode, server->id, SILC_ID_SERVER,
+                                   channel->cipher, 
+                                   channel->hmac_name,
+                                   channel->passphrase, NULL);
+    }
+
+    /* Change mode */
+    channel->mode = mode;
+
+    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) &&
+       channel->founder_key) {
+      silc_pkcs_public_key_free(channel->founder_key);
+      channel->founder_key = NULL;
+    }
+
     break;
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
@@ -686,14 +771,14 @@ void silc_server_notify(SilcServer server,
        if (!silc_server_client_on_channel(client, channel, &chl))
          goto out;
        
-       if (client != client2) {
+       if (client != client2 && server->server_type == SILC_ROUTER) {
          /* Sender must be operator */
-         if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+         if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+             !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
            SILC_LOG_DEBUG(("CUMODE change is not allowed"));
            goto out;
          }
 
-         /* Check that target is on channel */
          if (!silc_server_client_on_channel(client2, channel, &chl))
            goto out;
 
@@ -705,66 +790,103 @@ void silc_server_notify(SilcServer server,
        }
       }
 
-      /* Get entry to the channel user list */
-      silc_hash_table_list(channel->user_list, &htl);
-      while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-       /* If the mode is channel founder and we already find a client 
-          to have that mode on the channel we will enforce the sender
-          to change the channel founder mode away. There can be only one
-          channel founder on the channel. */
-       if (server->server_type == SILC_ROUTER &&
-           mode & SILC_CHANNEL_UMODE_CHANFO &&
-           chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-         SilcBuffer idp;
-         unsigned char cumode[4];
-
-         if (chl->client == client && chl->mode == mode) {
+      /* Get target channel user entry */
+      if (!silc_server_client_on_channel(client2, channel, &chl))
+       goto out;
+
+      if (mode & SILC_CHANNEL_UMODE_CHANFO &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
+         server->server_type == SILC_ROUTER &&
+         sock->user_data != server->router) {
+       SilcPublicKey founder_key = NULL;
+
+       /* If channel doesn't have founder auth mode then it's impossible
+          that someone would be getting founder rights with CUMODE command.
+          In that case there already either is founder or there isn't
+          founder at all on the channel. */
+       if (client && !(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
+         /* Force the mode to not have founder mode */
+         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+         silc_server_force_cumode_change(server, sock, channel, chl, mode);
+         notify_sent = TRUE;
+         break;
+       }
+
+       /* Get the founder of the channel and if found then this client
+          cannot be the founder since there already is one. */
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+         if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, sock, channel,
+                                           chl, mode);
            notify_sent = TRUE;
            break;
          }
-
+       silc_hash_table_list_reset(&htl);
+       if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+         break;
+
+       /* Founder not found of the channel.  Since the founder auth mode
+          is set on the channel now check whether this is the client that
+          originally set the mode. */
+
+       /* Get public key that must be present in notify */
+       tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+       if (!tmp || !silc_pkcs_public_key_decode(tmp, tmp_len,
+                                                &founder_key)) {
          mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-         silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
-                                        client2->id, SILC_ID_CLIENT,
-                                        client2->id);
-         
-         idp = silc_id_payload_encode(client2->id, SILC_ID_CLIENT);
-         SILC_PUT32_MSB(mode, cumode);
-         silc_server_send_notify_to_channel(server, sock, channel, FALSE, 
-                                            SILC_NOTIFY_TYPE_CUMODE_CHANGE,
-                                            3, idp->data, idp->len,
-                                            cumode, 4,
-                                            idp->data, idp->len);
-         silc_buffer_free(idp);
+         silc_server_force_cumode_change(server, sock, channel, chl, mode);
          notify_sent = TRUE;
+         break;
+       }
 
-         /* Force the mode change if we alredy set the mode */
-         if (chl2) {
-           chl2->mode = mode;
-           silc_free(channel_id);
-           silc_hash_table_list_reset(&htl);
-           goto out;
+       if (channel->founder_key) {
+         /* Now match the public key we have cached and public key sent.
+            They must match. */
+         if (client && client->data.public_key && 
+             !silc_pkcs_public_key_compare(channel->founder_key,
+                                           client->data.public_key)) {
+           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, sock, channel, chl, mode);
+           notify_sent = TRUE;
+           break;
          }
-       }
-       
-       if (chl->client == client2) {
-         if (chl->mode == mode) {
+         if (!silc_pkcs_public_key_compare(channel->founder_key,
+                                           founder_key)) {
+           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, sock, channel, chl, mode);
            notify_sent = TRUE;
            break;
          }
+       }
 
-         SILC_LOG_DEBUG(("Changing the channel user mode"));
-
-         /* Change the mode */
-         chl->mode = mode;
-         if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+       /* There cannot be anyone else as founder on the channel now.  This
+          client is definitely the founder due to this authentication */
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+         if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, NULL, channel, chl2,
+                                           chl2->mode);
            break;
-         
-         chl2 = chl;
-       }
+         }
+       silc_hash_table_list_reset(&htl);
+
+       if (founder_key)
+         silc_pkcs_public_key_free(founder_key);
       }
-      silc_hash_table_list_reset(&htl);
-      
+
+      if (server->server_type == SILC_ROUTER && chl->mode == mode) {
+       SILC_LOG_DEBUG(("Mode is changed already"));
+       break;
+      }
+
+      SILC_LOG_DEBUG(("Changing the channel user mode"));
+
+      /* Change the mode */
+      chl->mode = mode;
+
       /* Send the same notify to the channel */
       if (!notify_sent)
        silc_server_packet_send_to_channel(server, sock, channel, 
@@ -828,8 +950,9 @@ void silc_server_notify(SilcServer server,
     /* Get user's channel entry and check that inviting is allowed. */
     if (!silc_server_client_on_channel(client, channel, &chl))
       goto out;
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Inviting is not allowed"));
       goto out;
     }
@@ -937,7 +1060,7 @@ void silc_server_notify(SilcServer server,
       }
 
     if (channel_id2) {
-      SilcBuffer users = NULL, users_modes = NULL;
+      SilcBuffer modes = NULL, users = NULL, users_modes = NULL;
 
       /* Re-announce this channel which ID was changed. */
       silc_server_send_new_channel(server, sock, FALSE, channel->channel_name,
@@ -947,8 +1070,16 @@ void silc_server_notify(SilcServer server,
                                   channel->mode);
 
       /* Re-announce our clients on the channel as the ID has changed now */
-      silc_server_announce_get_channel_users(server, channel, &users,
+      silc_server_announce_get_channel_users(server, channel, &modes, &users,
                                             &users_modes);
+      if (modes) {
+       silc_buffer_push(modes, modes->data - modes->head);
+       silc_server_packet_send_dest(server, sock,
+                                    SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                    channel->id, SILC_ID_CHANNEL,
+                                    modes->data, modes->len, FALSE);
+       silc_buffer_free(modes);
+      }
       if (users) {
        silc_buffer_push(users, users->data - users->head);
        silc_server_packet_send(server, sock,
@@ -971,7 +1102,7 @@ void silc_server_notify(SilcServer server,
        silc_server_send_notify_topic_set(server, sock,
                                          server->server_type == SILC_ROUTER ?
                                          TRUE : FALSE, channel, 
-                                         channel->id, SILC_ID_CHANNEL,
+                                         server->id, SILC_ID_SERVER,
                                          channel->topic);
       }
     }
@@ -1047,6 +1178,15 @@ void silc_server_notify(SilcServer server,
            silc_server_remove_from_channels(server, NULL, client, 
                                             TRUE, NULL, FALSE);
 
+           /* Check if anyone is watching this nickname */
+           if (server->server_type == SILC_ROUTER)
+             silc_server_check_watcher_list(server, client, NULL,
+                                            SILC_NOTIFY_TYPE_SERVER_SIGNOFF);
+
+           /* Remove this client from watcher list if it is */
+           if (local)
+             silc_server_del_from_watcher_list(server, client);
+
            /* Remove the client */
            silc_idlist_del_client(local ? server->local_list :
                                   server->global_list, client);
@@ -1067,7 +1207,9 @@ void silc_server_notify(SilcServer server,
     silc_idlist_del_server(local ? server->local_list :
                           server->global_list, server_entry);
 
-    /* XXX update statistics */
+    /* Update statistics */
+    if (server->server_type == SILC_ROUTER)
+      server->stat.servers--;
 
     break;
 
@@ -1148,7 +1290,8 @@ void silc_server_notify(SilcServer server,
       /* Kicker must be operator on channel */
       if (!silc_server_client_on_channel(client2, channel, &chl))
        goto out;
-      if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+      if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
        SILC_LOG_DEBUG(("Kicking is not allowed"));
        goto out;
       }
@@ -1249,6 +1392,11 @@ void silc_server_notify(SilcServer server,
       silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
                                       FALSE);
 
+      /* Check if anyone is watching this nickname */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_check_watcher_list(server, client, NULL,
+                                      SILC_NOTIFY_TYPE_KILLED);
+
       break;
     }
 
@@ -1296,9 +1444,32 @@ void silc_server_notify(SilcServer server,
     if (mode & SILC_UMODE_DETACHED)
       client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
 
+    /* Update statistics */
+    if (server->server_type == SILC_ROUTER) {
+      if (mode & SILC_UMODE_GONE) {
+       if (!(client->mode & SILC_UMODE_GONE))
+         server->stat.aways++;
+      } else {
+       if (client->mode & SILC_UMODE_GONE)
+         server->stat.aways--;
+      }
+      if (mode & SILC_UMODE_DETACHED) {
+       if (!(client->mode & SILC_UMODE_DETACHED))
+         server->stat.detached++;
+      } else {
+       if (client->mode & SILC_UMODE_DETACHED)
+         server->stat.detached--;
+      }
+    }
+
     /* Change the mode */
     client->mode = mode;
 
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
     break;
 
   case SILC_NOTIFY_TYPE_BAN:
@@ -1367,6 +1538,42 @@ void silc_server_notify(SilcServer server,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_ERROR:
+    {
+      /*
+       * Error notify
+       */
+      SilcStatus error;
+
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!tmp && tmp_len != 1)
+       goto out;
+      error = (SilcStatus)tmp[0];
+
+      SILC_LOG_DEBUG(("ERROR notify (%d)", error));
+
+      if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
+         sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+       if (tmp) {
+         SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
+                         "the entry from cache"));
+         client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+         if (!client_id)
+           goto out;
+         client = silc_idlist_find_client_by_id(server->global_list, 
+                                                client_id, FALSE, NULL);
+         if (client) {
+           silc_server_remove_from_channels(server, NULL, client, TRUE, 
+                                            NULL, TRUE);
+           silc_idlist_del_client(server->global_list, client);
+         }
+         silc_free(client_id);
+       }
+      }
+    }
+    break;
+
     /* Ignore rest of the notify types for now */
   case SILC_NOTIFY_TYPE_NONE:
   case SILC_NOTIFY_TYPE_MOTD:
@@ -1458,35 +1665,37 @@ void silc_server_private_message(SilcServer server,
                                          &idata, &client);
   if (!dst_sock) {
     SilcBuffer idp;
+    unsigned char error;
 
     if (client && client->mode & SILC_UMODE_DETACHED) {
-      SILC_LOG_DEBUG(("Locally connected client is detached, "
-                     "discarding packet"));
+      SILC_LOG_DEBUG(("Client is detached, discarding packet"));
       return;
     }
 
-    /* Send IDENTIFY command reply with error status to indicate that
-       such destination ID does not exist or is invalid */
+    /* Send SILC_NOTIFY_TYPE_ERROR to indicate that such destination ID
+       does not exist or is invalid. */
     idp = silc_id_payload_encode_data(packet->dst_id,
                                      packet->dst_id_len,
                                      packet->dst_id_type);
     if (!idp)
       return;
 
+    error = SILC_STATUS_ERR_NO_SUCH_CLIENT_ID;
     if (packet->src_id_type == SILC_ID_CLIENT) {
       SilcClientID *client_id = silc_id_str2id(packet->src_id,
                                               packet->src_id_len,
                                               packet->src_id_type);
-      silc_server_send_dest_command_reply(server, sock, 
-                                         client_id, SILC_ID_CLIENT,
-                                         SILC_COMMAND_IDENTIFY,
-                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
-                                         0, 0, 1, 2, idp->data, idp->len);
+      silc_server_send_notify_dest(server, sock, FALSE,
+                                  client_id, SILC_ID_CLIENT,
+                                  SILC_NOTIFY_TYPE_ERROR, 2,
+                                  &error, 1,
+                                  idp->data, idp->len);
       silc_free(client_id);
     } else {
-      silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY,
-                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0,
-                                    0, 1, 2, idp->data, idp->len);
+      silc_server_send_notify(server, sock, FALSE,
+                             SILC_NOTIFY_TYPE_ERROR, 2,
+                             &error, 1,
+                             idp->data, idp->len);
     }
 
     silc_buffer_free(idp);
@@ -1644,7 +1853,34 @@ void silc_server_channel_message(SilcServer server,
   if (!channel) {
     channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
     if (!channel) {
-      SILC_LOG_DEBUG(("Could not find channel"));
+      SilcBuffer idp;
+      unsigned char error;
+
+      /* Send SILC_NOTIFY_TYPE_ERROR to indicate that such destination ID
+        does not exist or is invalid. */
+      idp = silc_id_payload_encode_data(packet->dst_id,
+                                       packet->dst_id_len,
+                                       packet->dst_id_type);
+      if (!idp)
+       goto out;
+
+      error = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
+      if (packet->src_id_type == SILC_ID_CLIENT) {
+       SilcClientID *client_id = silc_id_str2id(packet->src_id,
+                                                packet->src_id_len,
+                                                packet->src_id_type);
+       silc_server_send_notify_dest(server, sock, FALSE,
+                                    client_id, SILC_ID_CLIENT,
+                                    SILC_NOTIFY_TYPE_ERROR, 2,
+                                    &error, 1, idp->data, idp->len);
+       silc_free(client_id);
+      } else {
+       silc_server_send_notify(server, sock, FALSE,
+                               SILC_NOTIFY_TYPE_ERROR, 2,
+                               &error, 1, idp->data, idp->len);
+      }
+      
+      silc_buffer_free(idp);
       goto out;
     }
   }
@@ -1671,7 +1907,9 @@ void silc_server_channel_message(SilcServer server,
 
     /* If channel is moderated check that client is allowed to send
        messages. */
-    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chl->mode) {
+    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && 
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Channel is silenced from normal users"));
       goto out;
     }
@@ -1681,6 +1919,10 @@ void silc_server_channel_message(SilcServer server,
       SILC_LOG_DEBUG(("Channel is silenced from operators"));
       goto out;
     }
+    if (chl->mode & SILC_CHANNEL_UMODE_QUIET) {
+      SILC_LOG_DEBUG(("Sender is quieted on the channel"));
+      goto out;
+    }
 
     /* If the packet is coming from router, but the client entry is local 
        entry to us then some router is rerouting this to us and it is not 
@@ -1747,9 +1989,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   SilcBuffer buffer = packet->buffer;
   SilcClientEntry client;
   SilcClientID *client_id;
-  SilcBuffer reply;
   SilcIDListData idata;
-  char *username = NULL, *realname = NULL, *id_string;
+  char *username = NULL, *realname = NULL;
   SilcUInt16 username_len;
   SilcUInt32 id_len;
   int ret;
@@ -1768,8 +2009,10 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   /* Remove the old cache entry. */
   if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
     SILC_LOG_INFO(("Unauthenticated client attempted to register to network"));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "You have not been authenticated");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_NOT_AUTHENTICATED, NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
 
@@ -1784,8 +2027,11 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     silc_free(realname);
     SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
                    "connection", sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Incomplete client information");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION, 
+                                 NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
 
@@ -1794,8 +2040,11 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     silc_free(realname);
     SILC_LOG_ERROR(("Client %s (%s) did not send its username, closing "
                    "connection", sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Incomplete client information");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
 
@@ -1830,8 +2079,10 @@ SilcClientEntry silc_server_new_client(SilcServer server,
       SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
                      "connection", sock->hostname, sock->ip));
       silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: "
-                                   "Incomplete client information");
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   NULL);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
       return NULL;
     }
     
@@ -1850,8 +2101,10 @@ SilcClientEntry silc_server_new_client(SilcServer server,
       SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
                      "connection", sock->hostname, sock->ip));
       silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: "
-                                   "Incomplete client information");
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   NULL);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
       return NULL;
     }
     
@@ -1899,7 +2152,9 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     nickfail++;
     if (nickfail > 9) {
       silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: Bad nickname");
+                                   SILC_STATUS_ERR_BAD_NICKNAME, NULL);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
       return NULL;
     }
     snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
@@ -1923,23 +2178,26 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                            server->router->connection, 
                            server->server_type == SILC_ROUTER ? TRUE : FALSE,
                            client->id, SILC_ID_CLIENT, id_len);
-  
+
+  /* Distribute to backup routers */
+  if (server->server_type == SILC_ROUTER) {
+    SilcBuffer idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
+                           idp->data, idp->len, FALSE, TRUE);
+    silc_buffer_free(idp);
+  }
+
   /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
-  reply = silc_buffer_alloc_size(2 + 2 + id_len);
-  silc_buffer_format(reply,
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
-                    SILC_STR_UI_SHORT(id_len),
-                    SILC_STR_UI_XNSTRING(id_string, id_len),
-                    SILC_STR_END);
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
-                         reply->data, reply->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(reply);
+  silc_server_send_new_id(server, sock, FALSE, client->id, SILC_ID_CLIENT,
+                         silc_id_get_len(client->id, SILC_ID_CLIENT));
 
   /* Send some nice info to the client */
   silc_server_send_connect_notifys(server, sock, client);
 
+  /* Check if anyone is watching this nickname */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_check_watcher_list(server, client, NULL, 0);
+
   return client;
 }
 
@@ -1981,8 +2239,10 @@ SilcServerEntry silc_server_new_server(SilcServer server,
       SILC_LOG_INFO(("Unauthenticated %s attempted to register to "
                     "network", (sock->type == SILC_SOCKET_TYPE_SERVER ?
                                 "server" : "router")));
-      silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                   "You have not been authenticated");
+      silc_server_disconnect_remote(server, sock, 
+                                   SILC_STATUS_ERR_NOT_AUTHENTICATED, NULL);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
       return NULL;
     }
     local = FALSE;
@@ -1995,16 +2255,24 @@ SilcServerEntry silc_server_new_server(SilcServer server,
                                                         &name_len),
                             SILC_STR_END);
   if (ret == -1) {
-    if (id_string)
-      silc_free(id_string);
-    if (server_name)
-      silc_free(server_name);
+    silc_free(id_string);
+    silc_free(server_name);
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
 
   if (id_len > buffer->len) {
     silc_free(id_string);
     silc_free(server_name);
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
 
@@ -2016,6 +2284,11 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   if (!server_id) {
     silc_free(id_string);
     silc_free(server_name);
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
   silc_free(id_string);
@@ -2024,8 +2297,10 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   if (!silc_id_is_valid_server_id(server, server_id, sock)) {
     SILC_LOG_INFO(("Invalid server ID sent by %s (%s)",
                   sock->ip, sock->hostname));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Your Server ID is not valid");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_BAD_SERVER_ID, NULL);
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     silc_free(server_name);
     return NULL;
   }
@@ -2064,8 +2339,16 @@ SilcServerEntry silc_server_new_server(SilcServer server,
                            TRUE, new_server->id, SILC_ID_SERVER, 
                            silc_id_get_len(server_id, SILC_ID_SERVER));
 
-  if (server->server_type == SILC_ROUTER)
+  if (server->server_type == SILC_ROUTER) {
+    /* Distribute to backup routers */
+    SilcBuffer idp = silc_id_payload_encode(new_server->id, SILC_ID_SERVER);
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
+                           idp->data, idp->len, FALSE, TRUE);
+    silc_buffer_free(idp);
+
+    /* Statistics */
     server->stat.cell_servers++;
+  }
 
   /* Check whether this router connection has been replaced by an
      backup router. If it has been then we'll disable the server and will
@@ -2214,6 +2497,10 @@ static void silc_server_new_id_real(SilcServer server,
       if (sock->type == SILC_SOCKET_TYPE_SERVER)
        server->stat.cell_clients++;
       server->stat.clients++;
+
+      /* Check if anyone is watching this nickname */
+      if (server->server_type == SILC_ROUTER && id_list == server->local_list)
+       silc_server_check_watcher_list(server, entry, NULL, 0);
     }
     break;
 
@@ -2280,14 +2567,15 @@ static void silc_server_new_id_real(SilcServer server,
 
   /* If the sender of this packet is server and we are router we need to
      broadcast this packet to other routers in the network. */
-  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
+  if (broadcast && server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
-    silc_server_packet_send(server, server->router->connection,
-                           packet->type, 
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                           buffer->data, buffer->len, FALSE);
+    if (!server->standalone)
+      silc_server_packet_send(server, server->router->connection,
+                             packet->type, 
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             buffer->data, buffer->len, FALSE);
     silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                            packet->type, packet->flags,
                            packet->buffer->data, packet->buffer->len, 
@@ -2327,14 +2615,16 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
   /* If the sender of this packet is server and we are router we need to
      broadcast this packet to other routers in the network. Broadcast
      this list packet instead of multiple New ID packets. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
+  if (server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
-    silc_server_packet_send(server, server->router->connection,
-                           packet->type, 
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                           packet->buffer->data, packet->buffer->len, FALSE);
+    if (!server->standalone)
+      silc_server_packet_send(server, server->router->connection,
+                             packet->type, 
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             packet->buffer->data, 
+                             packet->buffer->len, FALSE);
     silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                            packet->type, packet->flags,
                            packet->buffer->data, packet->buffer->len, 
@@ -2438,9 +2728,16 @@ void silc_server_new_channel(SilcServer server,
                      silc_id_render(channel_id, SILC_ID_CHANNEL), 
                      sock->hostname));
     
-      silc_idlist_add_channel(server->global_list, strdup(channel_name), 
-                             0, channel_id, sock->user_data, NULL, NULL, 0);
+      channel = 
+       silc_idlist_add_channel(server->global_list, strdup(channel_name), 
+                               0, channel_id, sock->user_data, NULL, NULL, 0);
+      if (!channel)
+       return;
+      channel->disabled = TRUE;
+
       server->stat.channels++;
+      if (server->server_type == SILC_ROUTER)
+       channel->users_resolved = TRUE;
     }
   } else {
     /* The channel is coming from our server, thus it is in our cell
@@ -2487,9 +2784,7 @@ void silc_server_new_channel(SilcServer server,
        silc_free(channel_id);
        return;
       }
-
-      /* Get the mode and set it to the channel */
-      channel->mode = silc_channel_get_mode(payload);
+      channel->disabled = TRUE;
 
       /* Send the new channel key to the server */
       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
@@ -2503,12 +2798,11 @@ void silc_server_new_channel(SilcServer server,
       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
                              chk->data, chk->len, FALSE);
       silc_buffer_free(chk);
-
     } else {
       /* The channel exist by that name, check whether the ID's match.
         If they don't then we'll force the server to use the ID we have.
         We also create a new key for the channel. */
-      SilcBuffer users = NULL, users_modes = NULL;
+      SilcBuffer modes = NULL, users = NULL, users_modes = NULL;
 
       if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
@@ -2516,8 +2810,14 @@ void silc_server_new_channel(SilcServer server,
        SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
        silc_server_send_notify_channel_change(server, sock, FALSE, 
                                               channel_id, channel->id);
+
+       /* Wait that server re-announces this channel */
+       return;
       }
 
+#if 0 /* Lets expect that server send CMODE_CHANGE notify anyway to
+        (attempt) force mode change, and may very well get it. */
+
       /* If the mode is different from what we have then enforce the
         mode change. */
       mode = silc_channel_get_mode(payload);
@@ -2525,14 +2825,15 @@ void silc_server_new_channel(SilcServer server,
        SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
        silc_server_send_notify_cmode(server, sock, FALSE, channel,
                                      channel->mode, server->id,
-                                     SILC_ID_SERVER,
-                                     channel->cipher, channel->hmac_name,
-                                     channel->passphrase);
+                                     SILC_ID_SERVER, channel->cipher,
+                                     channel->hmac_name,
+                                     channel->passphrase,
+                                     channel->founder_key);
       }
+#endif
 
       /* Create new key for the channel and send it to the server and
         everybody else possibly on the channel. */
-
       if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
        if (!silc_server_create_channel_key(server, channel, 0))
          return;
@@ -2558,11 +2859,23 @@ void silc_server_new_channel(SilcServer server,
 
       silc_free(channel_id);
 
+      /* Update statistics */
+      server->stat.channels++;
+      server->stat.cell_channels++;
+
       /* Since the channel is coming from server and we also know about it
         then send the JOIN notify to the server so that it see's our
         users on the channel "joining" the channel. */
-      silc_server_announce_get_channel_users(server, channel, &users,
+      silc_server_announce_get_channel_users(server, channel, &modes, &users,
                                             &users_modes);
+      if (modes) {
+       silc_buffer_push(modes, modes->data - modes->head);
+       silc_server_packet_send_dest(server, sock,
+                                    SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                    channel->id, SILC_ID_CHANNEL,
+                                    modes->data, modes->len, FALSE);
+       silc_buffer_free(modes);
+      }
       if (users) {
        silc_buffer_push(users, users->data - users->head);
        silc_server_packet_send(server, sock,
@@ -2579,6 +2892,13 @@ void silc_server_new_channel(SilcServer server,
                                     users_modes->len, FALSE);
        silc_buffer_free(users_modes);
       }
+      if (channel->topic) {
+       silc_server_send_notify_topic_set(server, sock,
+                                         server->server_type == SILC_ROUTER ?
+                                         TRUE : FALSE, channel, 
+                                         server->id, SILC_ID_SERVER,
+                                         channel->topic);
+      }
     }
   }
 
@@ -2606,14 +2926,16 @@ void silc_server_new_channel_list(SilcServer server,
   /* If the sender of this packet is server and we are router we need to
      broadcast this packet to other routers in the network. Broadcast
      this list packet instead of multiple New Channel packets. */
-  if (!server->standalone && server->server_type == SILC_ROUTER &&
+  if (server->server_type == SILC_ROUTER &&
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
-    silc_server_packet_send(server, server->router->connection,
-                           packet->type, 
-                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                           packet->buffer->data, packet->buffer->len, FALSE);
+    if (!server->standalone)
+      silc_server_packet_send(server, server->router->connection,
+                             packet->type, 
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             packet->buffer->data, 
+                             packet->buffer->len, FALSE);
     silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                            packet->type, packet->flags,
                            packet->buffer->data, packet->buffer->len, 
@@ -2816,6 +3138,7 @@ typedef struct {
   SilcServer server;
   SilcSocketConnection sock;
   SilcPacketContext *packet;
+  void *data;
 } *SilcServerResumeResolve;
 
 SILC_SERVER_CMD_FUNC(resume_resolve)
@@ -2824,22 +3147,53 @@ SILC_SERVER_CMD_FUNC(resume_resolve)
   SilcServer server = r->server;
   SilcSocketConnection sock = r->sock;
   SilcServerCommandReplyContext reply = context2;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Start"));
 
-  if (!context2 || !silc_command_get_status(reply->payload, NULL, NULL)) {
+  if (!reply || !silc_command_get_status(reply->payload, NULL, NULL)) {
     SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
                    "closing connection", sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, 
-                                 "Server closed connection: "
-                                 "Incomplete resume information");
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 "Resuming not possible");
     goto out;
   }
 
+  if (reply && silc_command_get(reply->payload) == SILC_COMMAND_WHOIS) {
+    /* Get entry to the client, and resolve it if we don't have it. */
+    client = silc_idlist_find_client_by_id(server->local_list, 
+                                          r->data, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            r->data, TRUE, NULL);
+      if (!client) {
+       SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+                       "closing connection", sock->hostname, sock->ip));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+       goto out;
+      }
+    }
+
+    if (!(client->mode & SILC_UMODE_DETACHED)) {
+      SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+                     "closing connection", sock->hostname, sock->ip));
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      goto out;
+    }
+  }
+
   /* Reprocess the packet */
   silc_server_resume_client(server, sock, r->packet);
 
  out:
   silc_socket_free(r->sock);
   silc_packet_context_free(r->packet);
+  silc_free(r->data);
   silc_free(r);
 }
 
@@ -2859,9 +3213,14 @@ void silc_server_resume_client(SilcServer server,
   unsigned char *id_string, *auth = NULL;
   SilcUInt16 id_len, auth_len = 0;
   int ret, nickfail = 0;
-  bool resolved, local;
+  bool resolved, local, nick_change = FALSE, resolve = FALSE;
+  SilcChannelEntry channel;
+  SilcHashTableList htl;
+  SilcChannelClientEntry chl;
   SilcServerResumeResolve r;
 
+  SILC_LOG_DEBUG(("Start"));
+
   ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI16_NSTRING(&id_string, &id_len),
                             SILC_STR_END);
@@ -2871,9 +3230,6 @@ void silc_server_resume_client(SilcServer server,
   if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
     /* Client send this and is attempting to resume to old client session */
     SilcClientEntry client;
-    SilcChannelEntry channel;
-    SilcHashTableList htl;
-    SilcChannelClientEntry chl;
     SilcBuffer keyp;
 
     if (ret != -1) {
@@ -2886,8 +3242,9 @@ void silc_server_resume_client(SilcServer server,
     if (!client_id || auth_len < 128) {
       SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, "
                      "closing connection", sock->hostname, sock->ip));
-      silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                   "Incomplete resume information");
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
       return;
     }
 
@@ -2896,48 +3253,76 @@ void silc_server_resume_client(SilcServer server,
     idata = (SilcIDListData)client;
 
     /* Get entry to the client, and resolve it if we don't have it. */
-    detached_client = silc_server_get_client_resolve(server, client_id, 
+    detached_client = silc_server_get_client_resolve(server, client_id, FALSE,
                                                     &resolved);
     if (!detached_client) {
       if (resolved) {
        /* The client info is being resolved. Reprocess this packet after
           receiving the reply to the query. */
+       SILC_LOG_DEBUG(("Resolving client"));
        r = silc_calloc(1, sizeof(*r));
        if (!r)
          return;
-
        r->server = server;
        r->sock = silc_socket_dup(sock);
        r->packet = silc_packet_context_dup(packet);
+       r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
        silc_server_command_pending(server, SILC_COMMAND_WHOIS,
                                    server->cmd_ident,
                                    silc_server_command_resume_resolve, r);
       } else {
        SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
                        "closing connection", sock->hostname, sock->ip));
-       silc_server_disconnect_remote(server, sock, 
-                                     "Server closed connection: "
-                                     "Incomplete resume information");
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
       }
       return;
     }
 
-    /* Check that the client is detached */
-    if (!(detached_client->mode & SILC_UMODE_DETACHED)) {
-      SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
-                     "closing connection", sock->hostname, sock->ip));
-      silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                   "Incomplete resume information");
-      return;
+    if (!(detached_client->mode & SILC_UMODE_DETACHED))
+      resolve = TRUE;
+    if (!silc_hash_table_count(detached_client->channels) &&
+       detached_client->router)
+      resolve = TRUE;
+    if (!detached_client->nickname)
+      resolve = TRUE;
+
+    if (resolve) {
+      if (server->server_type == SILC_SERVER && !server->standalone) {
+       /* The client info is being resolved. Reprocess this packet after
+          receiving the reply to the query. */
+       SILC_LOG_DEBUG(("Resolving client info"));
+       silc_server_get_client_resolve(server, client_id, TRUE, NULL);
+       r = silc_calloc(1, sizeof(*r));
+       if (!r)
+         return;
+       r->server = server;
+       r->sock = silc_socket_dup(sock);
+       r->packet = silc_packet_context_dup(packet);
+       r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+       silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+                                   server->cmd_ident,
+                                   silc_server_command_resume_resolve, r);
+       return;
+      }
+      if (server->server_type == SILC_SERVER) {
+       SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+                       "closing connection", sock->hostname, sock->ip));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+       return;
+      }
     }
 
     /* Check that we have the public key of the client, if not then we must
        resolve it first. */
     if (!detached_client->data.public_key) {
-      if (server->standalone) {
-       silc_server_disconnect_remote(server, sock, 
-                                     "Server closed connection: "
-                                     "Incomplete resume information");
+      if (server->server_type == SILC_SERVER && server->standalone) {
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
       } else {
        /* We must retrieve the detached client's public key by sending
           GETKEY command. Reprocess this packet after receiving the key */
@@ -2945,10 +3330,12 @@ void silc_server_resume_client(SilcServer server,
        SilcSocketConnection dest_sock = 
          silc_server_get_client_route(server, NULL, 0, client_id, NULL, NULL);
 
+       SILC_LOG_DEBUG(("Resolving client public key"));
+
        silc_server_send_command(server, dest_sock ? dest_sock : 
                                 server->router->connection,
                                 SILC_COMMAND_GETKEY, ++server->cmd_ident,
-                                1, idp->data, idp->len);
+                                1, 1, idp->data, idp->len);
 
        r = silc_calloc(1, sizeof(*r));
        if (!r)
@@ -2964,23 +3351,34 @@ void silc_server_resume_client(SilcServer server,
        silc_buffer_free(idp);
       }
       return;
+    } else if (!silc_pkcs_public_key_compare(detached_client->data.public_key,
+                                            idata->public_key)) {
+      /* We require that the connection and resuming authentication data
+        must be using same key pair. */
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      return;
     }
 
     /* Verify the authentication payload.  This has to be successful in
        order to allow the resuming */
-    if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+    if (!idata->hash ||
+       !silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
                               detached_client->data.public_key, 0,
                               idata->hash, detached_client->id, 
                               SILC_ID_CLIENT)) {
       SILC_LOG_ERROR(("Client %s (%s) resume authentication failed, "
                      "closing connection", sock->hostname, sock->ip));
-      silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                   "Incomplete resume information");
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
       return;
     }
 
     /* Now resume the client to the network */
 
+    silc_schedule_task_del_by_context(server->schedule, detached_client);
     sock->user_data = detached_client;
     detached_client->connection = sock;
 
@@ -2990,26 +3388,39 @@ void silc_server_resume_client(SilcServer server,
     detached_client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
     detached_client->mode &= ~SILC_UMODE_DETACHED;
+    server->stat.my_detached--;
 
     /* Send the RESUME_CLIENT packet to our primary router so that others
        know this client isn't detached anymore. */
-    if (!server->standalone) {
-      buf = silc_buffer_alloc_size(2 + id_len);
-      silc_buffer_format(buf,
-                        SILC_STR_UI_SHORT(id_len),
-                        SILC_STR_UI_XNSTRING(id_string, id_len),
-                        SILC_STR_END);
+    buf = silc_buffer_alloc_size(2 + id_len);
+    silc_buffer_format(buf,
+                      SILC_STR_UI_SHORT(id_len),
+                      SILC_STR_UI_XNSTRING(id_string, id_len),
+                      SILC_STR_END);
+
+    /* Send to primary router */
+    if (!server->standalone)
       silc_server_packet_send(server, server->router->connection,
                              SILC_PACKET_RESUME_CLIENT, 0, 
                              buf->data, buf->len, TRUE);
-      silc_buffer_free(buf);
-    }
+
+    /* As router we must deliver this packet directly to the original
+       server whom this client was earlier. */
+    if (server->server_type == SILC_ROUTER && detached_client->router &&
+       detached_client->router->server_type != SILC_ROUTER)
+      silc_server_packet_send(server, detached_client->router->connection,
+                             SILC_PACKET_RESUME_CLIENT, 0, 
+                             buf->data, buf->len, TRUE);
+    silc_buffer_free(buf);
+
+    detached_client->router = NULL;
 
     /* Delete this client entry since we're resuming to old one. */
     server->stat.my_clients--;
     server->stat.clients--;
     if (server->stat.cell_clients)
       server->stat.cell_clients--;
+    silc_server_del_from_watcher_list(server, client);
     silc_idlist_del_client(server->local_list, client);
     client = detached_client;
 
@@ -3021,42 +3432,69 @@ void silc_server_resume_client(SilcServer server,
        nickfail++;
        if (nickfail > 9) {
          silc_server_disconnect_remote(server, sock, 
-                                       "Server closed connection: "
-                                       "Bad nickname");
+                                       SILC_STATUS_ERR_BAD_NICKNAME, NULL);
          return;
        }
        snprintf(&client->nickname[strlen(client->nickname) - 1], 1, 
                 "%d", nickfail);
       }
+      nick_change = TRUE;
+    }
 
+    if (nick_change) {
       /* Notify about Client ID change, nickname doesn't actually change. */
       if (!server->standalone)
        silc_server_send_notify_nick_change(server, server->router->connection,
                                            FALSE, client->id, client_id,
                                            client->nickname);
-      
-      silc_free(client->id);
-      client->id = client_id;
+    }
+
+    /* Resolve users on those channels that client has joined but we
+       haven't resolved user list yet. */
+    if (server->server_type == SILC_SERVER && !server->standalone) {
+      silc_hash_table_list(client->channels, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+       channel = chl->channel;
+       SILC_LOG_DEBUG(("Resolving users for %s channel", 
+                       channel->channel_name));
+       if (channel->disabled || !channel->users_resolved) {
+         silc_server_send_command(server, server->router->connection,
+                                  SILC_COMMAND_USERS, ++server->cmd_ident,
+                                  1, 2, channel->channel_name,
+                                  strlen(channel->channel_name));
+       }
+      }
+      silc_hash_table_list_reset(&htl);
+    }
+
+    /* Send the new client ID to the client. After this client may start
+       receiving other packets, and may start sending packets too. */
+    silc_server_send_new_id(server, sock, FALSE, client_id, SILC_ID_CLIENT,
+                           silc_id_get_len(client_id, SILC_ID_CLIENT));
+
+    if (nick_change) {
+      /* Send NICK change notify to channels as well. */
+      SilcBuffer oidp, nidp;
+      oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+      nidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+      silc_server_send_notify_on_channels(server, NULL, client, 
+                                         SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+                                         oidp->data, oidp->len, 
+                                         nidp->data, nidp->len,
+                                         client->nickname, 
+                                         strlen(client->nickname));
+      silc_buffer_free(oidp);
+      silc_buffer_free(nidp);
     }
 
     /* Add the client again to the ID cache to get it to correct list */
     if (!silc_idcache_del_by_context(server->local_list->clients, client))
       silc_idcache_del_by_context(server->global_list->clients, client);
+    silc_free(client->id);
+    client->id = client_id;
+    client_id = NULL;
     silc_idcache_add(server->local_list->clients, client->nickname,
-                    client->id, client, client->mode, NULL);
-
-    /* Send the new client ID to the client. */
-    id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
-    buf = silc_buffer_alloc_size(2 + 2 + id_len);
-    silc_buffer_format(buf,
-                      SILC_STR_UI_SHORT(SILC_ID_CLIENT),
-                      SILC_STR_UI_SHORT(id_len),
-                      SILC_STR_UI_XNSTRING(id_string, id_len),
-                      SILC_STR_END);
-    silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
-                           buf->data, buf->len, FALSE);
-    silc_free(id_string);
-    silc_buffer_free(buf);
+                    client->id, client, 0, NULL);
 
     /* Send some nice info to the client */
     silc_server_send_connect_notifys(server, sock, client);
@@ -3064,7 +3502,19 @@ void silc_server_resume_client(SilcServer server,
     /* Send all channel keys of channels the client has joined */
     silc_hash_table_list(client->channels, &htl);
     while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+      bool created = FALSE;
       channel = chl->channel;
+
+      if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
+       continue;
+
+      /* If we don't have channel key, then create one */
+      if (!channel->channel_key) {
+       if (!silc_server_create_channel_key(server, channel, 0))
+         continue;
+       created = TRUE;
+      }
+
       id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
       keyp = 
        silc_channel_key_payload_encode(silc_id_get_len(channel->id,
@@ -3079,12 +3529,17 @@ void silc_server_resume_client(SilcServer server,
       /* Send the key packet to client */
       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
                              keyp->data, keyp->len, FALSE);
+
+      if (created && server->server_type == SILC_SERVER && 
+         !server->standalone)
+       silc_server_packet_send(server, server->router->connection, 
+                               SILC_PACKET_CHANNEL_KEY, 0, 
+                               keyp->data, keyp->len, FALSE);
+
+      silc_buffer_free(keyp);
     }
     silc_hash_table_list_reset(&htl);
 
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("Your session is successfully resumed"));
-
   } else if (sock->type != SILC_SOCKET_TYPE_CLIENT) {
     /* Server or router sent this to us to notify that that a client has
        been resumed. */
@@ -3146,29 +3601,50 @@ void silc_server_resume_client(SilcServer server,
       }
     }
 
+    if (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER && 
+       server_entry->server_type == SILC_ROUTER)
+      local = FALSE;
+
+    SILC_LOG_DEBUG(("Resuming detached client"));
+
     /* Change the client to correct list. */
     if (!silc_idcache_del_by_context(server->local_list->clients,
                                     detached_client))
       silc_idcache_del_by_context(server->global_list->clients,
                                  detached_client);
-    silc_idcache_add(local ? server->local_list->clients :
-                    server->global_list->clients, detached_client->nickname,
+    silc_idcache_add(local && server->server_type == SILC_ROUTER ? 
+                    server->local_list->clients : 
+                    server->global_list->clients, 
+                    detached_client->nickname,
                     detached_client->id, detached_client, FALSE, NULL);
 
     /* Change the owner of the client if needed */
     if (detached_client->router != server_entry)
       detached_client->router = server_entry;
 
+    /* Update channel information regarding global clients on channel. */
+    if (server->server_type == SILC_SERVER) {
+      silc_hash_table_list(detached_client->channels, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void **)&chl))
+       chl->channel->global_users = 
+         silc_server_channel_has_global(chl->channel);
+      silc_hash_table_list_reset(&htl);
+    }
+
+    silc_schedule_task_del_by_context(server->schedule, detached_client);
+
     /* If the sender of this packet is server and we are router we need to
        broadcast this packet to other routers in the network. */
-    if (!server->standalone && server->server_type == SILC_ROUTER &&
+    if (server->server_type == SILC_ROUTER &&
        sock->type == SILC_SOCKET_TYPE_SERVER &&
        !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
       SILC_LOG_DEBUG(("Broadcasting received Resume Client packet"));
-      silc_server_packet_send(server, server->router->connection,
-                             packet->type, 
-                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                             buffer->data, buffer->len, FALSE);
+      if (!server->standalone)
+       silc_server_packet_send(server, server->router->connection,
+                               packet->type, 
+                               packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                               buffer->data, buffer->len, FALSE);
       silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                              packet->type, packet->flags,
                              packet->buffer->data, packet->buffer->len,