Merged silc_1_0_branch to trunk.
[silc.git] / apps / silcd / packet_receive.c
index 0956be2dad1444fa88339f903582ffdc6e3bf2ee..b9a0224f052f99174af426457632390cdc458960 100644 (file)
@@ -2,15 +2,15 @@
 
   packet_receive.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2003 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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -25,9 +25,7 @@
 #include "serverincludes.h"
 #include "server_internal.h"
 
-extern char *server_version;
-
-/* Received notify packet. Server can receive notify packets from router. 
+/* Received notify packet. Server can receive notify packets from router.
    Server then relays the notify messages to clients if needed. */
 
 void silc_server_notify(SilcServer server,
@@ -40,25 +38,23 @@ void silc_server_notify(SilcServer server,
   SilcChannelID *channel_id = NULL, *channel_id2;
   SilcClientID *client_id, *client_id2;
   SilcServerID *server_id;
-  SilcChannelEntry channel;
-  SilcClientEntry client;
-  SilcServerEntry server_entry;
+  SilcIdType id_type;
+  SilcChannelEntry channel = NULL;
+  SilcClientEntry client = NULL, client2 = NULL;
+  SilcServerEntry server_entry = NULL;
   SilcChannelClientEntry chl;
-  SilcIDCacheEntry cache;
+  SilcIDCacheEntry cache = NULL;
   SilcHashTableList htl;
-  uint32 mode;
+  SilcUInt32 mode;
   unsigned char *tmp;
-  uint32 tmp_len;
+  SilcUInt32 tmp_len;
   bool local;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
-
-  if (!packet->dst_id)
+      packet->src_id_type != SILC_ID_SERVER || !packet->dst_id) {
+    SILC_LOG_DEBUG(("Bad notify packet received"));
     return;
+  }
 
   /* If the packet is destined directly to a client then relay the packet
      before processing it. */
@@ -68,11 +64,12 @@ void silc_server_notify(SilcServer server,
 
     /* Get the route to the client */
     dst_sock = silc_server_get_client_route(server, packet->dst_id,
-                                           packet->dst_id_len, NULL, &idata);
+                                           packet->dst_id_len, NULL,
+                                           &idata, NULL);
     if (dst_sock)
       /* Relay the packet */
       silc_server_relay_packet(server, dst_sock, idata->send_key,
-                              idata->hmac_receive, idata->psn_send++,
+                              idata->hmac_send, idata->psn_send++,
                               packet, TRUE);
   }
 
@@ -86,7 +83,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,27 +94,27 @@ 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, 
+      silc_server_packet_send_dest(server, SILC_PRIMARY_ROUTE(server),
+                                  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->buffer->data,
+                                  packet->buffer->len, FALSE);
+      silc_server_backup_send_dest(server, sock->user_data,
                                   packet->type, packet->flags,
                                   channel_id, SILC_ID_CHANNEL,
-                                  packet->buffer->data, packet->buffer->len, 
+                                  packet->buffer->data, packet->buffer->len,
                                   FALSE, TRUE);
     } else {
       /* Packet is destined to client or server */
-      silc_server_packet_send(server, server->router->connection, 
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
                              packet->type,
-                             packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                             packet->buffer->data, packet->buffer->len, 
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             packet->buffer->data, packet->buffer->len,
                              FALSE);
-      silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+      silc_server_backup_send(server, sock->user_data,
                              packet->type, packet->flags,
-                             packet->buffer->data, packet->buffer->len, 
+                             packet->buffer->data, packet->buffer->len,
                              FALSE, TRUE);
     }
   }
@@ -129,26 +126,30 @@ void silc_server_notify(SilcServer server,
 
   switch(type) {
   case SILC_NOTIFY_TYPE_JOIN:
-    /* 
+    /*
      * Distribute the notify to local clients on the channel
      */
     SILC_LOG_DEBUG(("JOIN notify"));
 
+    if (channel_id)
+      silc_free(channel_id);
+
     /* Get Channel ID */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
     if (!tmp)
       goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -159,28 +160,30 @@ 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);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* If the the client is not in local list we check global list (ie. the
        channel will be global channel) and if it does not exist then create
        entry for the client. */
-    client = silc_idlist_find_client_by_id(server->global_list, 
-                                          client_id, server->server_type, 
-                                          NULL);
+    client = silc_idlist_find_client_by_id(server->global_list,
+                                          client_id, server->server_type,
+                                          &cache);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
+      client = silc_idlist_find_client_by_id(server->local_list,
                                             client_id, server->server_type,
-                                            NULL);
+                                            &cache);
       if (!client) {
        /* If router did not find the client the it is bogus */
-       if (server->server_type != SILC_SERVER)
+       if (server->server_type != SILC_SERVER) {
+         silc_free(client_id);
          goto out;
+       }
 
-       client = 
+       client =
          silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
-                                silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                silc_id_dup(client_id, SILC_ID_CLIENT),
                                 sock->user_data, NULL, 0);
        if (!client) {
          SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
@@ -191,48 +194,69 @@ void silc_server_notify(SilcServer server,
        client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
       }
     }
+    silc_free(client_id);
 
     /* Do not process the notify if the client is not registered */
     if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
       break;
 
     /* Do not add client to channel if it is there already */
-    if (silc_server_client_on_channel(client, channel)) {
-      SILC_LOG_DEBUG(("Client already on channel"));
+    if (silc_server_client_on_channel(client, channel, NULL)) {
+      SILC_LOG_DEBUG(("Client already on channel %s",
+                     channel->channel_name));
       break;
     }
 
     /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+                                      FALSE, TRUE, packet->buffer->data,
                                       packet->buffer->len, FALSE);
 
-    if (server->server_type != SILC_ROUTER && 
+    if (server->server_type != SILC_ROUTER &&
        sock->type == SILC_SOCKET_TYPE_ROUTER)
       /* The channel is global now */
       channel->global_users = TRUE;
 
     SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name));
 
-    /* JOIN the global client to the channel (local clients (if router 
+    /* JOIN the global client to the channel (local clients (if router
        created the channel) is joined in the pending JOIN command). */
     chl = silc_calloc(1, sizeof(*chl));
     chl->client = client;
     chl->channel = channel;
 
-    /* If this is the first one on the channel then it is the founder of
-       the channel. */
-    if (!silc_hash_table_count(channel->user_list))
-      chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+    if (server->server_type != SILC_ROUTER ||
+       sock->type == SILC_SOCKET_TYPE_ROUTER) {
+      /* If this is the first one on the channel then it is the founder of
+        the channel. This is done on normal server and on router if this
+        notify is coming from router */
+      if (!silc_hash_table_count(channel->user_list)) {
+       SILC_LOG_DEBUG(("Client %s is founder on channel",
+                       silc_id_render(chl->client->id, SILC_ID_CLIENT)));
+       chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+      }
+    }
 
     silc_hash_table_add(channel->user_list, client, chl);
     silc_hash_table_add(client->channels, channel, chl);
-    silc_free(client_id);
+    channel->user_count++;
+    channel->disabled = FALSE;
+
+    /* Make sure we don't expire clients that are on channel */
+    if (cache)
+      cache->expire = 0;
+
+    /* Update statistics */
+    if (server->server_type == SILC_ROUTER) {
+      if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+       server->stat.cell_chanclients++;
+      server->stat.chanclients++;
+    }
 
     break;
 
   case SILC_NOTIFY_TYPE_LEAVE:
-    /* 
+    /*
      * Distribute the notify to local clients on the channel
      */
     SILC_LOG_DEBUG(("LEAVE notify"));
@@ -245,12 +269,13 @@ void silc_server_notify(SilcServer server,
     }
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
-    if (!channel) { 
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -262,17 +287,17 @@ void silc_server_notify(SilcServer server,
       silc_free(channel_id);
       goto out;
     }
-    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id) {
       silc_free(channel_id);
       goto out;
     }
 
     /* Get client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
+    client = silc_idlist_find_client_by_id(server->global_list,
                                           client_id, TRUE, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
+      client = silc_idlist_find_client_by_id(server->local_list,
                                             client_id, TRUE, NULL);
       if (!client) {
        silc_free(client_id);
@@ -281,14 +306,15 @@ void silc_server_notify(SilcServer server,
       }
     }
     silc_free(client_id);
+    silc_free(channel_id);
 
     /* Check if on channel */
-    if (!silc_server_client_on_channel(client, channel))
+    if (!silc_server_client_on_channel(client, channel, NULL))
       break;
 
     /* Send the leave notify to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+                                      FALSE, TRUE, packet->buffer->data,
                                       packet->buffer->len, FALSE);
 
     /* Remove the user from channel */
@@ -296,7 +322,7 @@ void silc_server_notify(SilcServer server,
     break;
 
   case SILC_NOTIFY_TYPE_SIGNOFF:
-    /* 
+    /*
      * Distribute the notify to local clients on the channel
      */
     SILC_LOG_DEBUG(("SIGNOFF notify"));
@@ -305,15 +331,15 @@ 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);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Get client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
+    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, 
+      client = silc_idlist_find_client_by_id(server->local_list,
                                             client_id, TRUE, &cache);
       if (!client) {
        silc_free(client_id);
@@ -329,25 +355,65 @@ void silc_server_notify(SilcServer server,
 
     /* Update statistics */
     server->stat.clients--;
-    if (server->server_type == SILC_ROUTER)
+    if (server->stat.cell_clients)
       server->stat.cell_clients--;
     SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
     SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+    silc_schedule_task_del_by_context(server->schedule, client);
 
     /* Remove the client from all channels. */
-    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
+    silc_server_remove_from_channels(server, NULL, client, TRUE,
+                                    tmp, FALSE, 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);
+
+    /* Remove this client from watcher list if it is */
+    silc_server_del_from_watcher_list(server, client);
 
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
     break;
 
   case SILC_NOTIFY_TYPE_TOPIC_SET:
-    /* 
+    /*
      * Distribute the notify to local clients on the channel
      */
 
     SILC_LOG_DEBUG(("TOPIC SET notify"));
 
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+    if (!client_id)
+      goto out;
+
+    /* Get client entry */
+    if (id_type == SILC_ID_CLIENT) {
+      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,
+                                              client_id, TRUE, &cache);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+      silc_free(client_id);
+    }
+
+    /* Get the topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+
     if (!channel_id) {
       channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
                                  packet->dst_id_type);
@@ -356,84 +422,100 @@ void silc_server_notify(SilcServer server,
     }
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
     }
+    silc_free(channel_id);
 
-    /* Get the topic */
-    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp) {
-      silc_free(channel_id);
+    if (channel->topic && !strcmp(channel->topic, tmp)) {
+      SILC_LOG_DEBUG(("Topic is already set and same"));
       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 */
     silc_free(channel->topic);
     channel->topic = strdup(tmp);
 
     /* Send the same notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
+    silc_server_packet_send_to_channel(server, NULL, channel, packet->type,
+                                      FALSE, TRUE, packet->buffer->data,
                                       packet->buffer->len, FALSE);
-    silc_free(channel_id);
     break;
 
   case SILC_NOTIFY_TYPE_NICK_CHANGE:
     {
-      /* 
+      /*
        * Distribute the notify to local clients on the channel
        */
       unsigned char *id, *id2;
+      char *nickname;
+      SilcUInt32 nickname_len;
 
       SILC_LOG_DEBUG(("NICK CHANGE notify"));
-      
+
       /* Get old client ID */
       id = silc_argument_get_arg_type(args, 1, &tmp_len);
       if (!id)
        goto out;
-      client_id = silc_id_payload_parse_id(id, tmp_len);
+      client_id = silc_id_payload_parse_id(id, tmp_len, NULL);
       if (!client_id)
        goto out;
-      
+
       /* Get new client ID */
       id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
       if (!id2)
        goto out;
-      client_id2 = silc_id_payload_parse_id(id2, tmp_len);
-      if (!client_id2)
+      client_id2 = silc_id_payload_parse_id(id2, tmp_len, NULL);
+      if (!client_id2) {
+       silc_free(client_id);
        goto out;
-      
-      SILC_LOG_DEBUG(("Old Client ID id(%s)", 
+      }
+
+      SILC_LOG_DEBUG(("Old Client ID id(%s)",
                      silc_id_render(client_id, SILC_ID_CLIENT)));
-      SILC_LOG_DEBUG(("New Client ID id(%s)", 
+      SILC_LOG_DEBUG(("New Client ID id(%s)",
                      silc_id_render(client_id2, SILC_ID_CLIENT)));
 
+      /* From protocol version 1.1 we also get the new nickname */
+      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_id2);
+      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_id2);
+       client = silc_idlist_replace_client_id(server,
+                                              server->local_list, client_id,
+                                              client_id2, nickname);
 
       if (client) {
-       /* The nickname is not valid anymore, set it NULL. This causes that
-          the nickname will be queried if someone wants to know it. */
-       if (client->nickname)
-         silc_free(client->nickname);
-       client->nickname = NULL;
-
        /* 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_NOTIFY_TYPE_NICK_CHANGE, 2,
-                                           id, tmp_len, 
-                                           id2, tmp_len);
+       silc_server_send_notify_on_channels(server, client, client,
+                                           SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+                                           id, tmp_len, id2, tmp_len,
+                                           nickname, nickname ?
+                                           nickname_len : 0);
       }
 
       silc_free(client_id);
@@ -443,12 +525,35 @@ void silc_server_notify(SilcServer server,
     }
 
   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
-    /* 
+    /*
      * Distribute the notify to local clients on the channel
      */
-    
+
     SILC_LOG_DEBUG(("CMODE CHANGE notify"));
-      
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+    if (!client_id)
+      goto out;
+
+    /* Get client entry */
+    if (id_type == SILC_ID_CLIENT) {
+      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,
+                                              client_id, TRUE, &cache);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+    }
+    silc_free(client_id);
+
     if (!channel_id) {
       channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
                                  packet->dst_id_type);
@@ -457,34 +562,158 @@ void silc_server_notify(SilcServer server,
     }
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
     }
+    silc_free(channel_id);
 
     /* Get the mode */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (!tmp) {
-      silc_free(channel_id);
+    if (!tmp)
       goto out;
-    }
-
     SILC_GET32_MSB(mode, tmp);
 
     /* Check if mode changed */
-    if (channel->mode == mode)
+    if (channel->mode == mode) {
+      SILC_LOG_DEBUG(("Mode is changed already"));
+
+      /* If this mode change has founder mode then we'll enforce the
+        change so that the server gets the real founder public key */
+      if (server->server_type != SILC_SERVER &&
+         sock != SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_FOUNDER_AUTH && channel->founder_key) {
+       SILC_LOG_DEBUG(("Sending founder public key to server"));
+       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, NULL);
+      }
+
+      /* If we received same mode from our primary check whether founder
+        mode and key in the notify is set.  We update the founder key
+        here since we may have wrong one */
+      if (server->server_type == SILC_SERVER &&
+         sock == SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+       SILC_LOG_DEBUG(("Founder public key received from router"));
+       tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
+       if (!tmp)
+         break;
+
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+       silc_pkcs_public_key_payload_decode(tmp, tmp_len,
+                                           &channel->founder_key);
+      }
+
+      /* Check also for channel public key list */
+      if (server->server_type == SILC_SERVER &&
+         sock == SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+       SilcBuffer chpklist;
+       SilcBuffer sidp;
+       unsigned char mask[4];
+
+       SILC_LOG_DEBUG(("Channel public key list received from router"));
+       tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
+       if (!tmp)
+         break;
+
+       /* Set the router's list, and send the notify to channel too so that
+          channel gets the list */
+       silc_server_set_channel_pk_list(server, sock, channel, tmp, tmp_len);
+       chpklist = silc_server_get_channel_pk_list(server, channel,
+                                                  FALSE, FALSE);
+       if (!chpklist)
+         break;
+       sidp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+       SILC_PUT32_MSB(channel->mode, mask);
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+                                          SILC_NOTIFY_TYPE_CMODE_CHANGE, 7,
+                                          sidp->data, sidp->len,
+                                          mask, 4,
+                                          channel->cipher,
+                                          channel->cipher ?
+                                          strlen(channel->cipher) : 0,
+                                          channel->hmac_name,
+                                          channel->hmac_name ?
+                                          strlen(channel->hmac_name) : 0,
+                                          channel->passphrase,
+                                          channel->passphrase ?
+                                          strlen(channel->passphrase) : 0,
+                                          NULL, 0,
+                                          chpklist->data, chpklist->len);
+       silc_buffer_free(sidp);
+       silc_buffer_free(chpklist);
+       goto out;
+      }
+
       break;
+    }
 
-    /* Send the same notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
+    /* Get user's channel entry and check that mode change is allowed */
+    if (client) {
+      if (!silc_server_client_on_channel(client, channel, &chl))
+       goto out;
+      if (!silc_server_check_cmode_rights(server, channel, chl, mode)) {
+       SILC_LOG_DEBUG(("CMODE change is not allowed"));
+       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, NULL);
+       goto out;
+      }
+    } else {
+      /* Assure that server is not removing founder mode from us */
+      if (server->server_type == SILC_ROUTER &&
+         sock != SILC_PRIMARY_ROUTE(server) &&
+         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, NULL);
+       goto out;
+      }
+
+      /* If server is adding founder mode, check whether there is founder
+        on channel already and is not from this server */
+      if (server->server_type == SILC_ROUTER &&
+         sock != SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+         if (chl->mode & SILC_CHANNEL_UMODE_CHANFO &&
+             chl->client->router != sock->user_data) {
+           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, NULL);
+           silc_hash_table_list_reset(&htl);
+           goto out;
+         }
+       silc_hash_table_list_reset(&htl);
+      }
+    }
 
     /* If the channel had private keys set and the mode was removed then
        we must re-generate and re-distribute a new channel key */
@@ -493,18 +722,14 @@ void silc_server_notify(SilcServer server,
       /* Re-generate channel key */
       if (!silc_server_create_channel_key(server, channel, 0))
        goto out;
-      
+
       /* Send the channel key. This sends it to our local clients and if
         we are normal server to our router as well. */
-      silc_server_send_channel_key(server, NULL, channel, 
-                                  server->server_type == SILC_ROUTER ? 
+      silc_server_send_channel_key(server, NULL, channel,
+                                  server->server_type == SILC_ROUTER ?
                                   FALSE : !server->standalone);
     }
 
-    /* Change mode */
-    channel->mode = mode;
-    silc_free(channel_id);
-
     /* Get the hmac */
     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
     if (tmp) {
@@ -517,10 +742,9 @@ 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);
-      silc_hmac_set_key(channel->hmac, hash, 
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+                    channel->key_len / 8, hash);
+      silc_hmac_set_key(channel->hmac, hash,
                        silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
     }
@@ -529,21 +753,118 @@ void silc_server_notify(SilcServer server,
     tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
     if (tmp) {
       silc_free(channel->passphrase);
-      channel->passphrase = strdup(tmp);
+      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;
+      if (!silc_pkcs_public_key_payload_decode(tmp, tmp_len,
+                                              &channel->founder_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, NULL);
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+      }
+    }
+
+    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, NULL);
+    }
+
+    /* Process channel public key(s). */
+    tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
+    if (tmp && mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) {
+      SilcStatus ret =
+       silc_server_set_channel_pk_list(server, sock, channel, tmp, tmp_len);
+
+      /* If list was set already we will enforce the same list to server. */
+      if (ret == SILC_STATUS_ERR_OPERATION_ALLOWED) {
+       SilcBuffer chpklist = silc_server_get_channel_pk_list(server, channel,
+                                                             TRUE, FALSE);
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     mode, server->id, SILC_ID_SERVER,
+                                     channel->cipher,
+                                     channel->hmac_name,
+                                     channel->passphrase, NULL,
+                                     chpklist);
+       silc_buffer_free(chpklist);
+      }
+    }
+
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, NULL, channel, packet->type,
+                                      FALSE, TRUE, packet->buffer->data,
+                                      packet->buffer->len, FALSE);
+
+    /* Change mode */
+    channel->mode = mode;
+
+    /* Cleanup if some modes are removed */
+
+    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) &&
+       channel->founder_key) {
+      silc_pkcs_public_key_free(channel->founder_key);
+      channel->founder_key = NULL;
+    }
+
+    if (!(channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) &&
+       channel->channel_pubkeys) {
+      silc_hash_table_free(channel->channel_pubkeys);
+      channel->channel_pubkeys = NULL;
     }
 
     break;
 
   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
     {
-      /* 
+      /*
        * Distribute the notify to local clients on the channel
        */
       SilcChannelClientEntry chl2 = NULL;
       bool notify_sent = FALSE;
-      
+
       SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
-      
+
+      /* Get client ID */
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!tmp)
+       goto out;
+      client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+      if (!client_id)
+       goto out;
+
+      /* Get client entry */
+      if (id_type == SILC_ID_CLIENT) {
+       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,
+                                                client_id, TRUE, &cache);
+         if (!client) {
+           silc_free(client_id);
+           goto out;
+         }
+       }
+      }
+      silc_free(client_id);
+
       if (!channel_id) {
        channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
                                    packet->dst_id_type);
@@ -552,12 +873,13 @@ void silc_server_notify(SilcServer server,
       }
 
       /* Get channel entry */
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->global_list,
                                               channel_id, NULL);
       if (!channel) {
-       channel = silc_idlist_find_channel_by_id(server->local_list, 
+       channel = silc_idlist_find_channel_by_id(server->local_list,
                                                 channel_id, NULL);
        if (!channel) {
+         SILC_LOG_DEBUG(("Notify for unknown channel"));
          silc_free(channel_id);
          goto out;
        }
@@ -569,97 +891,164 @@ void silc_server_notify(SilcServer server,
        silc_free(channel_id);
        goto out;
       }
-      
+
       SILC_GET32_MSB(mode, tmp);
-      
+
       /* Get target client */
       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
       if (!tmp)
        goto out;
-      client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
       if (!client_id)
        goto out;
-      
+
       /* Get client entry */
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, TRUE, NULL);
-      if (!client) {
-       client = silc_idlist_find_client_by_id(server->local_list, 
-                                              client_id, TRUE, NULL);
-       if (!client) {
+      client2 = silc_idlist_find_client_by_id(server->global_list,
+                                             client_id, TRUE, NULL);
+      if (!client2) {
+       client2 = silc_idlist_find_client_by_id(server->local_list,
+                                               client_id, TRUE, NULL);
+       if (!client2) {
          silc_free(client_id);
          goto out;
        }
       }
       silc_free(client_id);
 
-      /* 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) {
-           notify_sent = TRUE;
-           break;
+      if (client) {
+       /* Check that sender is on channel */
+       if (!silc_server_client_on_channel(client, channel, &chl))
+         goto out;
+
+       if (client != client2 && server->server_type == SILC_ROUTER) {
+         /* Sender must be operator */
+         if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+             !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+           SILC_LOG_DEBUG(("CUMODE change is not allowed"));
+           goto out;
          }
 
-         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-         silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
-                                        client->id, SILC_ID_CLIENT,
-                                        client->id);
-         
-         idp = silc_id_payload_encode(client->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);
-         notify_sent = TRUE;
+         if (!silc_server_client_on_channel(client2, channel, &chl))
+           goto out;
 
-         /* 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);
+         /* If target is founder mode change is not allowed. */
+         if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           SILC_LOG_DEBUG(("CUMODE change is not allowed"));
            goto out;
          }
        }
-       
-       if (chl->client == client) {
-         if (chl->mode == mode) {
+      }
+
+      /* Get target channel user entry */
+      if (!silc_server_client_on_channel(client2, channel, &chl))
+       goto out;
+
+      if (server->server_type == SILC_SERVER && chl->mode == mode) {
+       SILC_LOG_DEBUG(("Mode is changed already"));
+       break;
+      }
+
+      /* Check whether to give founder rights to this user or not.  The
+        problem here is that we get only the public key of the client,
+        but no authentication data.  We must assume that server has
+        already authenticated the user (and thus we must trust the
+        server). */
+      if (mode & SILC_CHANNEL_UMODE_CHANFO &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
+         server->server_type == SILC_ROUTER &&
+         sock != SILC_PRIMARY_ROUTE(server)) {
+       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 (valid only when 'client' is
+          valid). */
+       if (client && !(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
+         /* Force the mode to not have founder mode */
+         chl->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) {
+           chl->mode = 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. */
+
+       if (channel->founder_key) {
+         /* 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_payload_decode(tmp, tmp_len,
+                                                          &founder_key)) {
+           chl->mode = 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))
+         /* Now match the public key we have cached and public key sent.
+            They must match. */
+         if (!silc_pkcs_public_key_compare(channel->founder_key,
+                                           founder_key)) {
+           chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, sock, channel, chl, mode);
+           notify_sent = TRUE;
            break;
-         
-         chl2 = chl;
+         }
        }
+
+       /* There cannot be anyone else as founder on the channel now.  This
+          client is definitely the founder due to this 'authentication'.
+          We trust the server did the actual authentication earlier. */
+       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;
+         }
+       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_SERVER && chl->mode == mode) {
+       SILC_LOG_DEBUG(("Mode is changed already"));
+       break;
+      }
+
+      SILC_LOG_DEBUG(("Changing %s channel user mode",
+                     chl->client->nickname ? chl->client->nickname :
+                     (unsigned char *)""));
+
+      /* 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, 
-                                          packet->type, 
-                                          FALSE, packet->buffer->data, 
+       silc_server_packet_send_to_channel(server, NULL, channel,
+                                          packet->type,
+                                          FALSE, TRUE, packet->buffer->data,
                                           packet->buffer->len, FALSE);
-      
+
       silc_free(channel_id);
       break;
     }
@@ -675,64 +1064,100 @@ void silc_server_notify(SilcServer server,
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
     }
     silc_free(channel_id);
 
-    /* Get the added invite */
+#if 0 /* These aren't actually used anywhere or needed, since this
+        notify is for handling the invite list (direct invite
+        goes to client and is not handled here at all). */
+
+    /* Get client ID */
     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (tmp) {
-      if (!channel->invite_list)
-       channel->invite_list = silc_calloc(tmp_len + 2, 
-                                          sizeof(*channel->invite_list));
-      else
-       channel->invite_list = silc_realloc(channel->invite_list, 
-                                           sizeof(*channel->invite_list) * 
-                                           (tmp_len + 
-                                            strlen(channel->invite_list) + 
-                                            2));
-      if (tmp[tmp_len - 1] == ',')
-       tmp[tmp_len - 1] = '\0';
-      
-      strncat(channel->invite_list, tmp, tmp_len);
-      strncat(channel->invite_list, ",", 1);
-    }
-
-    /* Get the deleted invite */
-    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
-    if (tmp && channel->invite_list) {
-      char *start, *end, *n;
-      
-      if (!strncmp(channel->invite_list, tmp, 
-                  strlen(channel->invite_list) - 1)) {
-       silc_free(channel->invite_list);
-       channel->invite_list = NULL;
-      } else {
-       start = strstr(channel->invite_list, tmp);
-       if (start && strlen(start) >= tmp_len) {
-         end = start + tmp_len;
-         n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n));
-         strncat(n, channel->invite_list, start - channel->invite_list);
-         strncat(n, end + 1, ((channel->invite_list + 
-                               strlen(channel->invite_list)) - end) - 1);
-         silc_free(channel->invite_list);
-         channel->invite_list = n;
-       }
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+    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,
+                                            client_id, TRUE, &cache);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
       }
     }
+    silc_free(client_id);
+
+    /* Get user's channel entry and check that inviting is allowed. */
+    if (server->server_type == SILC_ROUTER) {
+      if (!silc_server_client_on_channel(client, channel, &chl))
+        goto out;
+      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;
+      }
+    }
+#endif
+
+    /* Get the invite action */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp && tmp_len == 1) {
+      SilcUInt8 action = (SilcUInt8)tmp[0];
+      SilcUInt16 iargc = 0;
+      SilcArgumentPayload iargs;
+
+      /* Get invite list */
+      tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+      if (!tmp || tmp_len < 2)
+       goto out;
+
+      /* Parse the arguments to see they are constructed correctly */
+      SILC_GET16_MSB(iargc, tmp);
+      iargs = silc_argument_payload_parse(tmp + 2, tmp_len - 2, iargc);
+      if (!iargs)
+       goto out;
+
+      if (action != 0x01 && !channel->invite_list)
+       channel->invite_list =
+         silc_hash_table_alloc(0, silc_hash_ptr,
+                               NULL, NULL, NULL,
+                               silc_server_inviteban_destruct, channel, TRUE);
+
+      /* Proces the invite action */
+      silc_server_inviteban_process(server, channel->invite_list, action,
+                                   iargs);
+      silc_argument_payload_free(iargs);
+
+      /* If we are router we must send this notify to our local servers on
+         the channel.  Normal server does nothing.  The notify is not
+         sent to clients. */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_packet_send_to_channel(server, sock, channel,
+                                          packet->type, FALSE, FALSE,
+                                          packet->buffer->data,
+                                          packet->buffer->len, FALSE);
+    }
 
     break;
 
@@ -751,38 +1176,39 @@ void silc_server_notify(SilcServer server,
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
 
     /* Get the channel entry */
-    channel = silc_idlist_find_channel_by_id(server->local_list, 
+    channel = silc_idlist_find_channel_by_id(server->local_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->global_list, 
+      channel = silc_idlist_find_channel_by_id(server->global_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
     }
 
     /* Send the notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+                                      FALSE, TRUE, packet->buffer->data,
                                       packet->buffer->len, FALSE);
 
     /* Get the new Channel ID */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
     if (!tmp)
       goto out;
-    channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id2 = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id2)
       goto out;
 
-    SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
+    SILC_LOG_DEBUG(("Old Channel ID id(%s)",
                    silc_id_render(channel_id, SILC_ID_CHANNEL)));
-    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+    SILC_LOG_DEBUG(("New Channel ID id(%s)",
                    silc_id_render(channel_id2, SILC_ID_CHANNEL)));
 
     /* Replace the Channel ID */
@@ -795,17 +1221,17 @@ 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,
-                                  channel->id, 
-                                  silc_id_get_len(channel->id, 
+                                  channel->id,
+                                  silc_id_get_len(channel->id,
                                                   SILC_ID_CHANNEL),
                                   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 (users) {
        silc_buffer_push(users, users->data - users->head);
@@ -814,12 +1240,20 @@ void silc_server_notify(SilcServer server,
                                users->data, users->len, FALSE);
        silc_buffer_free(users);
       }
+      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_modes) {
        silc_buffer_push(users_modes, users_modes->data - users_modes->head);
        silc_server_packet_send_dest(server, sock,
                                     SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
                                     channel->id, SILC_ID_CHANNEL,
-                                    users_modes->data, 
+                                    users_modes->data,
                                     users_modes->len, FALSE);
        silc_buffer_free(users_modes);
       }
@@ -828,8 +1262,8 @@ void silc_server_notify(SilcServer server,
       if (channel->topic) {
        silc_server_send_notify_topic_set(server, sock,
                                          server->server_type == SILC_ROUTER ?
-                                         TRUE : FALSE, channel, 
-                                         channel->id, SILC_ID_CHANNEL,
+                                         TRUE : FALSE, channel,
+                                         server->id, SILC_ID_SERVER,
                                          channel->topic);
       }
     }
@@ -839,26 +1273,38 @@ void silc_server_notify(SilcServer server,
     break;
 
   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
-    /* 
+    /*
      * Remove the server entry and all clients that this server owns.
      */
 
     SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
 
+    /* Backup router shouldn't accept SERVER_SIGNOFF's from normal routers
+       when the backup isn't acting as primary router. */
+    if (sock->type == SILC_SOCKET_TYPE_SERVER &&
+       server->backup_router && server->server_type == SILC_BACKUP_ROUTER)
+      return;
+
     /* Get Server ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!server_id)
       goto out;
 
+    /* If the ID is mine, this notify is not allowed. */
+    if (SILC_ID_SERVER_COMPARE(server_id, server->id)) {
+      SILC_LOG_DEBUG(("Ignoring my own ID for SERVER_SIGNOFF"));
+      break;
+    }
+
     /* Get server entry */
-    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+    server_entry = silc_idlist_find_server_by_id(server->global_list,
                                                 server_id, TRUE, NULL);
-    local = TRUE;
+    local = FALSE;
     if (!server_entry) {
-      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+      server_entry = silc_idlist_find_server_by_id(server->local_list,
                                                   server_id, TRUE, NULL);
       local = TRUE;
       if (!server_entry) {
@@ -875,18 +1321,18 @@ void silc_server_notify(SilcServer server,
            tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
            if (!tmp)
              continue;
-           client_id = silc_id_payload_parse_id(tmp, tmp_len);
+           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
            if (!client_id)
              continue;
 
            /* Get client entry */
-           client = silc_idlist_find_client_by_id(server->global_list, 
+           client = silc_idlist_find_client_by_id(server->global_list,
                                                   client_id, TRUE, &cache);
-           local = TRUE;
+           local = FALSE;
            if (!client) {
-             client = silc_idlist_find_client_by_id(server->local_list, 
+             client = silc_idlist_find_client_by_id(server->local_list,
                                                     client_id, TRUE, &cache);
-             local = FALSE;
+             local = TRUE;
              if (!client) {
                silc_free(client_id);
                continue;
@@ -896,16 +1342,26 @@ void silc_server_notify(SilcServer server,
 
            /* Update statistics */
            server->stat.clients--;
-           if (server->server_type == SILC_ROUTER)
+           if (server->stat.cell_clients)
              server->stat.cell_clients--;
            SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
            SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 
            /* Remove the client from all channels. */
-           silc_server_remove_from_channels(server, NULL, client, 
-                                            TRUE, NULL, FALSE);
+           silc_server_remove_from_channels(server, NULL, client,
+                                            TRUE, NULL, FALSE, 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_data(client);
            silc_idlist_del_client(local ? server->local_list :
                                   server->global_list, client);
          }
@@ -917,25 +1373,50 @@ void silc_server_notify(SilcServer server,
     }
     silc_free(server_id);
 
-    /* Free all client entries that this server owns as they will
-       become invalid now as well. */
-    silc_server_remove_clients_by_server(server, server_entry, TRUE);
+    /* For local entrys SERVER_SIGNOFF is processed only on backup router.
+       It is possible that router sends server signoff for a server.  If
+       backup router has it as local connection it will be closed. */
+    if (SILC_IS_LOCAL(server_entry)) {
+      if (server->server_type == SILC_BACKUP_ROUTER) {
+       sock = server_entry->connection;
+       SILC_LOG_DEBUG(("Closing connection %s after SERVER_SIGNOFF",
+                      sock->hostname));
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       SILC_SET_DISCONNECTING(sock);
+       silc_server_close_connection(server, sock);
+      }
+
+      break;
+    }
+
+    /* Remove all servers that are originated from this server, and
+       remove the clients of those servers too. */
+    silc_server_remove_servers_by_server(server, server_entry, TRUE);
+
+    /* Remove the clients that this server owns as they will become
+       invalid now too. */
+    silc_server_remove_clients_by_server(server, server_entry->router,
+                                        server_entry, TRUE);
+    silc_server_backup_del(server, server_entry);
 
     /* Remove the server entry */
     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;
 
   case SILC_NOTIFY_TYPE_KICKED:
-    /* 
+    /*
      * Distribute the notify to local clients on the channel
      */
-    
+
     SILC_LOG_DEBUG(("KICKED notify"));
-      
+
     if (!channel_id) {
       channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
                                  packet->dst_id_type);
@@ -944,12 +1425,13 @@ void silc_server_notify(SilcServer server,
     }
 
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -960,27 +1442,76 @@ 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);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* If the the client is not in local list we check global list */
-    client = silc_idlist_find_client_by_id(server->global_list, 
+    client = silc_idlist_find_client_by_id(server->global_list,
                                           client_id, TRUE, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
+      client = silc_idlist_find_client_by_id(server->local_list,
                                             client_id, TRUE, NULL);
       if (!client) {
        silc_free(client_id);
        goto out;
       }
     }
+    silc_free(client_id);
+
+    /* If target is founder they cannot be kicked */
+    if (!silc_server_client_on_channel(client, channel, &chl))
+      goto out;
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO)
+      goto out;
+
+    /* Get the kicker's Client ID */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+    if (!client_id)
+      goto out;
+
+    /* If the the client is not in local list we check global list */
+    client2 = silc_idlist_find_client_by_id(server->global_list,
+                                           client_id, TRUE, NULL);
+    if (!client2) {
+      client2 = silc_idlist_find_client_by_id(server->local_list,
+                                             client_id, TRUE, NULL);
+      if (!client2) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
+
+    /* Kicker must be operator on channel */
+    if (!silc_server_client_on_channel(client2, channel, &chl))
+      goto out;
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      SILC_LOG_DEBUG(("Kicking is not allowed"));
+      goto out;
+    }
 
     /* Send to channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+                                      FALSE, TRUE, packet->buffer->data,
                                       packet->buffer->len, FALSE);
 
+    /* Remove the client from channel's invite list */
+    if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+      SilcBuffer ab;
+      SilcArgumentPayload iargs;
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      ab = silc_argument_payload_encode_one(NULL, tmp, tmp_len, 3);
+      iargs = silc_argument_payload_parse(ab->data, ab->len, 1);
+      silc_server_inviteban_process(server, channel->invite_list, 1, iargs);
+      silc_buffer_free(ab);
+      silc_argument_payload_free(iargs);
+    }
+
     /* Remove the client from channel */
     silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
 
@@ -988,28 +1519,28 @@ void silc_server_notify(SilcServer server,
 
   case SILC_NOTIFY_TYPE_KILLED:
     {
-      /* 
+      /*
        * Distribute the notify to local clients on channels
        */
-      unsigned char *id;
-      uint32 id_len;
-    
+      unsigned char *id, *comment;
+      SilcUInt32 id_len, comment_len;
+
       SILC_LOG_DEBUG(("KILLED notify"));
-      
+
       /* Get client ID */
       id = silc_argument_get_arg_type(args, 1, &id_len);
       if (!id)
        goto out;
-      client_id = silc_id_payload_parse_id(id, id_len);
+      client_id = silc_id_payload_parse_id(id, id_len, NULL);
       if (!client_id)
        goto out;
 
       /* If the the client is not in local list we check global list */
-      client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, TRUE, NULL);
+      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, 
-                                              client_id, TRUE, NULL);
+       client = silc_idlist_find_client_by_id(server->local_list,
+                                              client_id, TRUE, &cache);
        if (!client) {
          silc_free(client_id);
          goto out;
@@ -1027,22 +1558,71 @@ void silc_server_notify(SilcServer server,
       }
 
       /* Get comment */
-      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-      if (tmp_len > 128)
-       tmp = NULL;
+      comment = silc_argument_get_arg_type(args, 2, &comment_len);
+      if (comment_len > 128)
+       comment_len = 127;
+
+      /* Get the killer's Client ID */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (!tmp)
+       goto out;
+      client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+      if (!client_id)
+       goto out;
+
+      if (id_type == SILC_ID_CLIENT) {
+       /* If the the client is not in local list we check global list */
+       client2 = silc_idlist_find_client_by_id(server->global_list,
+                                               client_id, TRUE, NULL);
+       if (!client2) {
+         client2 = silc_idlist_find_client_by_id(server->local_list,
+                                                 client_id, TRUE, NULL);
+         if (!client2) {
+           silc_free(client_id);
+           goto out;
+         }
+       }
+       silc_free(client_id);
+
+       /* Killer must be router operator */
+       if (server->server_type != SILC_SERVER &&
+           !(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+         SILC_LOG_DEBUG(("Killing is not allowed"));
+         goto out;
+       }
+      }
 
       /* Send the notify to local clients on the channels except to the
         client who is killed. */
       silc_server_send_notify_on_channels(server, client, client,
-                                         SILC_NOTIFY_TYPE_KILLED, 
-                                         tmp ? 2 : 1,
-                                         id, id_len, 
+                                         SILC_NOTIFY_TYPE_KILLED, 3,
+                                         id, id_len, comment, comment_len,
                                          tmp, tmp_len);
 
       /* Remove the client from all channels */
-      silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
-                                      FALSE);
+      silc_server_remove_from_channels(server, NULL, client, FALSE, NULL,
+                                      FALSE, TRUE);
+
+      /* Check if anyone is watching this nickname */
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_KILLED);
+
+      /* Update statistics */
+      server->stat.clients--;
+      if (server->stat.cell_clients)
+       server->stat.cell_clients--;
+      SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+      SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+      if (SILC_IS_LOCAL(client)) {
+       server->stat.my_clients--;
+       silc_schedule_task_del_by_context(server->schedule, client);
+       silc_idlist_del_data(client);
+       client->mode = 0;
+      }
 
+      client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+      cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
       break;
     }
 
@@ -1057,15 +1637,15 @@ 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);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!client_id)
       goto out;
 
     /* Get client entry */
-    client = silc_idlist_find_client_by_id(server->global_list, 
+    client = silc_idlist_find_client_by_id(server->global_list,
                                           client_id, TRUE, NULL);
     if (!client) {
-      client = silc_idlist_find_client_by_id(server->local_list, 
+      client = silc_idlist_find_client_by_id(server->local_list,
                                             client_id, TRUE, NULL);
       if (!client) {
        silc_free(client_id);
@@ -1080,32 +1660,38 @@ void silc_server_notify(SilcServer server,
       goto out;
     SILC_GET32_MSB(mode, tmp);
 
-#define SILC_UMODE_STATS_UPDATE(oper, mod)     \
-do {                                           \
-    if (client->mode & (mod)) {                        \
-      if (!(mode & (mod))) {                   \
-       if (client->connection)                 \
-         server->stat.my_ ## oper ## _ops--;   \
-        if (server->server_type == SILC_ROUTER)        \
-         server->stat. oper ## _ops--;         \
-      }                                                \
-    } else {                                   \
-      if (mode & (mod)) {                      \
-       if (client->connection)                 \
-         server->stat.my_ ## oper ## _ops++;   \
-        if (server->server_type == SILC_ROUTER)        \
-         server->stat. oper ## _ops++;         \
-      }                                                \
-    }                                          \
-} while(0)
+    /* Remove internal resumed flag if client is marked detached now */
+    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--;
+      }
+    }
     SILC_UMODE_STATS_UPDATE(server, SILC_UMODE_SERVER_OPERATOR);
     SILC_UMODE_STATS_UPDATE(router, SILC_UMODE_ROUTER_OPERATOR);
 
-    /* Save the mode */
+    /* 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:
@@ -1114,61 +1700,101 @@ do {                                           \
      */
 
     SILC_LOG_DEBUG(("BAN notify"));
-    
+
     /* Get Channel ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
       goto out;
-    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
     if (!channel_id)
       goto out;
-    
+
     /* Get channel entry */
-    channel = silc_idlist_find_channel_by_id(server->global_list, 
+    channel = silc_idlist_find_channel_by_id(server->global_list,
                                             channel_id, NULL);
     if (!channel) {
-      channel = silc_idlist_find_channel_by_id(server->local_list, 
+      channel = silc_idlist_find_channel_by_id(server->local_list,
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
     }
     silc_free(channel_id);
 
-    /* Get the new ban and add it to the ban list */
+    /* Get the ban action */
     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
-    if (tmp) {
-      if (!channel->ban_list)
-       channel->ban_list = silc_calloc(tmp_len + 2, 
-                                       sizeof(*channel->ban_list));
-      else
-       channel->ban_list = silc_realloc(channel->ban_list, 
-                                        sizeof(*channel->ban_list) * 
-                                        (tmp_len + 
-                                         strlen(channel->ban_list) + 2));
-      strncat(channel->ban_list, tmp, tmp_len);
-      strncat(channel->ban_list, ",", 1);
+    if (tmp && tmp_len == 1) {
+      SilcUInt8 action = (SilcUInt8)tmp[0];
+      SilcUInt16 iargc = 0;
+      SilcArgumentPayload iargs;
+
+      /* Get ban list */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (!tmp || tmp_len < 2)
+       goto out;
+
+      /* Parse the arguments to see they are constructed correctly */
+      SILC_GET16_MSB(iargc, tmp);
+      iargs = silc_argument_payload_parse(tmp + 2, tmp_len - 2, iargc);
+      if (!iargs)
+       goto out;
+
+      if (action != 0x01 && !channel->ban_list)
+       channel->ban_list =
+         silc_hash_table_alloc(0, silc_hash_ptr,
+                               NULL, NULL, NULL,
+                               silc_server_inviteban_destruct, channel, TRUE);
+
+      /* Proces the ban action */
+      silc_server_inviteban_process(server, channel->ban_list, action,
+                                   iargs);
+      silc_argument_payload_free(iargs);
+
+      /* If we are router we must send this notify to our local servers on
+         the channel.  Normal server does nothing.  The notify is not
+         sent to clients. */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_packet_send_to_channel(server, sock, channel,
+                                          packet->type, FALSE, FALSE,
+                                          packet->buffer->data,
+                                          packet->buffer->len, FALSE);
     }
+    break;
 
-    /* Get the ban to be removed and remove it from the list */
-    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
-    if (tmp && channel->ban_list) {
-      char *start, *end, *n;
-      
-      if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) {
-       silc_free(channel->ban_list);
-       channel->ban_list = NULL;
-      } else {
-       start = strstr(channel->ban_list, tmp);
-       if (start && strlen(start) >= tmp_len) {
-         end = start + tmp_len;
-         n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
-         strncat(n, channel->ban_list, start - channel->ban_list);
-         strncat(n, end + 1, ((channel->ban_list + 
-                               strlen(channel->ban_list)) - end) - 1);
-         silc_free(channel->ban_list);
-         channel->ban_list = n;
+  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, FALSE);
+           silc_idlist_del_data(client);
+           silc_idlist_del_client(server->global_list, client);
+         }
+         silc_free(client_id);
        }
       }
     }
@@ -1192,7 +1818,7 @@ void silc_server_notify_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  uint16 len;
+  SilcUInt16 len;
 
   SILC_LOG_DEBUG(("Processing Notify List"));
 
@@ -1239,7 +1865,7 @@ void silc_server_notify_list(SilcServer server,
   silc_free(new);
 }
 
-/* Received private message. This resolves the destination of the message 
+/* Received private message. This resolves the destination of the message
    and sends the packet. This is used by both server and router.  If the
    destination is our locally connected client this sends the packet to
    the client. This may also send the message for further routing if
@@ -1251,6 +1877,7 @@ void silc_server_private_message(SilcServer server,
 {
   SilcSocketConnection dst_sock;
   SilcIDListData idata;
+  SilcClientEntry client;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1260,36 +1887,54 @@ void silc_server_private_message(SilcServer server,
 
   /* Get the route to the client */
   dst_sock = silc_server_get_client_route(server, packet->dst_id,
-                                         packet->dst_id_len, NULL, &idata);
+                                         packet->dst_id_len, NULL,
+                                         &idata, &client);
   if (!dst_sock) {
-    /* Send IDENTIFY command reply with error status to indicate that
-       such destination ID does not exist or is invalid */
-    SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id,
-                                                packet->dst_id_len,
-                                                packet->dst_id_type);
+    SilcBuffer idp;
+    unsigned char error;
+
+    if (client && client->mode & SILC_UMODE_DETACHED) {
+      SILC_LOG_DEBUG(("Client is detached, discarding packet"));
+      return;
+    }
+
+    /* 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, 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, 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);
     return;
   }
 
+  /* Check whether destination client wishes to receive private messages */
+  if (client && !(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) &&
+      client->mode & SILC_UMODE_BLOCK_PRIVMSG) {
+    SILC_LOG_DEBUG(("Client blocks private messages, discarding packet"));
+    return;
+  }
+
   /* Send the private message */
   silc_server_send_private_message(server, dst_sock, idata->send_key,
                                   idata->hmac_send, idata->psn_send++,
@@ -1319,7 +1964,8 @@ void silc_server_private_message_key(SilcServer server,
 
   /* Get the route to the client */
   dst_sock = silc_server_get_client_route(server, packet->dst_id,
-                                         packet->dst_id_len, NULL, &idata);
+                                         packet->dst_id_len, NULL,
+                                         &idata, NULL);
   if (!dst_sock)
     return;
 
@@ -1329,7 +1975,7 @@ void silc_server_private_message_key(SilcServer server,
 }
 
 /* Processes incoming command reply packet. The command reply packet may
-   be destined to one of our clients or it may directly for us. We will 
+   be destined to one of our clients or it may directly for us. We will
    call the command reply routine after processing the packet. */
 
 void silc_server_command_reply(SilcServer server,
@@ -1344,11 +1990,6 @@ void silc_server_command_reply(SilcServer server,
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Source must be server or router */
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    return;
-
   if (packet->dst_id_type == SILC_ID_CHANNEL)
     return;
 
@@ -1378,20 +2019,24 @@ void silc_server_command_reply(SilcServer server,
 
   if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
     /* Relay the packet to the client */
-    
+    const SilcBufferStruct p;
+
     dst_sock = (SilcSocketConnection)client->connection;
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    
-    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
     idata = (SilcIDListData)client;
-    
+
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len
+                    + packet->dst_id_len + packet->padlen);
+    if (!silc_packet_send_prepare(dst_sock, 0, 0, buffer->len,
+                                  idata->hmac_send, (const SilcBuffer)&p)) {
+      SILC_LOG_ERROR(("Cannot send packet"));
+      return;
+    }
+    silc_buffer_put((SilcBuffer)&p, buffer->data, buffer->len);
+
     /* Encrypt packet */
     silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
-                       dst_sock->outbuf, buffer->len);
-    
+                       (SilcBuffer)&p, buffer->len);
+
     /* Send the packet */
     silc_server_packet_send_real(server, dst_sock, TRUE);
 
@@ -1408,8 +2053,9 @@ void silc_server_channel_message(SilcServer server,
 {
   SilcChannelEntry channel = NULL;
   SilcChannelID *id = NULL;
-  void *sender = NULL;
-  void *sender_entry = NULL;
+  void *sender_id = NULL;
+  SilcClientEntry sender_entry = NULL;
+  SilcChannelClientEntry chl;
   bool local = TRUE;
 
   SILC_LOG_DEBUG(("Processing channel message"));
@@ -1428,34 +2074,81 @@ 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;
     }
   }
 
   /* See that this client is on the channel. If the original sender is
      not client (as it can be server as well) we don't do the check. */
-  sender = silc_id_str2id(packet->src_id, packet->src_id_len, 
-                         packet->src_id_type);
-  if (!sender)
+  sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                            packet->src_id_type);
+  if (!sender_id)
     goto out;
   if (packet->src_id_type == SILC_ID_CLIENT) {
-    sender_entry = silc_idlist_find_client_by_id(server->local_list, 
-                                                sender, TRUE, NULL);
+    sender_entry = silc_idlist_find_client_by_id(server->local_list,
+                                                sender_id, TRUE, NULL);
     if (!sender_entry) {
       local = FALSE;
-      sender_entry = silc_idlist_find_client_by_id(server->global_list, 
-                                                  sender, TRUE, NULL);
+      sender_entry = silc_idlist_find_client_by_id(server->global_list,
+                                                  sender_id, TRUE, NULL);
     }
-    if (!sender_entry || !silc_server_client_on_channel(sender_entry, 
-                                                       channel)) {
+    if (!sender_entry || !silc_server_client_on_channel(sender_entry,
+                                                       channel, &chl)) {
       SILC_LOG_DEBUG(("Client not on 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 allowed. */
+    /* If channel is moderated check that client is allowed to send
+       messages. */
+    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;
+    }
+    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
+       chl->mode & SILC_CHANNEL_UMODE_CHANOP &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      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
+       allowed. When the client is local to us it means that we've routed
+       this packet to network, and now someone is routing it back to us. */
     if (server->server_type == SILC_ROUTER &&
        sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
       SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
@@ -1465,16 +2158,14 @@ void silc_server_channel_message(SilcServer server,
 
   /* Distribute the packet to our local clients. This will send the
      packet for further routing as well, if needed. */
-  silc_server_packet_relay_to_channel(server, sock, channel, sender,
+  silc_server_packet_relay_to_channel(server, sock, channel, sender_id,
                                      packet->src_id_type, sender_entry,
                                      packet->buffer->data,
                                      packet->buffer->len, FALSE);
 
  out:
-  if (sender)
-    silc_free(sender);
-  if (id)
-    silc_free(id);
+  silc_free(sender_id);
+  silc_free(id);
 }
 
 /* Received channel key packet. We distribute the key to all of our locally
@@ -1488,22 +2179,25 @@ void silc_server_channel_key(SilcServer server,
   SilcChannelEntry channel;
 
   if (packet->src_id_type != SILC_ID_SERVER ||
-      (server->server_type == SILC_ROUTER &&
+      (server->server_type == SILC_ROUTER && !server->backup_router &&
        sock->type == SILC_SOCKET_TYPE_ROUTER))
     return;
 
   /* Save the channel key */
   channel = silc_server_save_channel_key(server, buffer, NULL);
-  if (!channel)
+  if (!channel) {
+    SILC_LOG_ERROR(("Bad channel key from %s (%s)",
+                   sock->hostname, sock->ip));
     return;
+  }
 
   /* Distribute the key to everybody who is on the channel. If we are router
      we will also send it to locally connected servers. */
   silc_server_send_channel_key(server, sock, channel, FALSE);
-  
+
   if (server->server_type != SILC_BACKUP_ROUTER) {
     /* Distribute to local cell backup routers. */
-    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+    silc_server_backup_send(server, sock->user_data,
                            SILC_PACKET_CHANNEL_KEY, 0,
                            buffer->data, buffer->len, FALSE, TRUE);
   }
@@ -1519,11 +2213,10 @@ 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;
-  uint16 username_len;
-  uint32 id_len;
+  char *username = NULL, *realname = NULL;
+  SilcUInt16 username_len;
+  SilcUInt32 id_len;
   int ret;
   char *hostname, *nickname;
   int nickfail = 0;
@@ -1539,31 +2232,53 @@ 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_ERROR(("Lost client's cache entry - bad thing"));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Unknown client");
+    SILC_LOG_INFO(("Unauthenticated client attempted to register to network"));
+    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;
+  }
+
+  /* Make sure this client hasn't registered already */
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED) {
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                 "Too many registrations");
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     return NULL;
   }
 
   /* Parse incoming packet */
   ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI16_NSTRING_ALLOC(&username, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&username,
                                                         &username_len),
                             SILC_STR_UI16_STRING_ALLOC(&realname),
                             SILC_STR_END);
   if (ret == -1) {
     silc_free(username);
     silc_free(realname);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Incomplete client information");
+    SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
+                   "connection", sock->hostname, sock->ip));
+    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 (!username) {
     silc_free(username);
     silc_free(realname);
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Incomplete client information");
+    SILC_LOG_ERROR(("Client %s (%s) did not send its username, closing "
+                   "connection", sock->hostname, sock->ip));
+    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;
   }
 
@@ -1588,38 +2303,45 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     int tlen = strcspn(username, "@");
     char *phostname = NULL;
 
-    hostname = silc_calloc((strlen(username) - tlen) + 1, sizeof(char));
-    memcpy(hostname, username + tlen + 1, strlen(username) - tlen - 1);
+    hostname = silc_memdup(username + tlen + 1, strlen(username) - tlen - 1);
 
-    if (strcmp(sock->hostname, sock->ip) && 
+    if (strcmp(sock->hostname, sock->ip) &&
        strcmp(sock->hostname, hostname)) {
       silc_free(username);
       silc_free(hostname);
       silc_free(realname);
-      silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: "
-                                   "Incomplete client information");
+      SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
+                     "connection", sock->hostname, sock->ip));
+      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;
     }
-    
+
     pident = silc_pkcs_decode_identifier(client->data.public_key->identifier);
     if (pident) {
       phostname = strdup(pident->host);
       silc_pkcs_free_identifier(pident);
     }
 
-    if (!strcmp(sock->hostname, sock->ip) && 
+    if (!strcmp(sock->hostname, sock->ip) &&
        phostname && strcmp(phostname, hostname)) {
       silc_free(username);
       silc_free(hostname);
       silc_free(phostname);
       silc_free(realname);
-      silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: "
-                                   "Incomplete client information");
+      SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
+                     "connection", sock->hostname, sock->ip));
+      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(phostname);
   } else {
     /* The hostname is not present, add it. */
@@ -1630,7 +2352,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
 #if 0
     if (strcmp(sock->hostname, sock->ip)) {
 #endif
-      newusername = silc_calloc(strlen(username) + 
+      newusername = silc_calloc(strlen(username) +
                                strlen(sock->hostname) + 2,
                                sizeof(*newusername));
       strncat(newusername, username, strlen(username));
@@ -1640,11 +2362,11 @@ SilcClientEntry silc_server_new_client(SilcServer server,
       username = newusername;
 #if 0
     } else {
-      SilcPublicKeyIdentifier pident = 
+      SilcPublicKeyIdentifier pident =
        silc_pkcs_decode_identifier(client->data.public_key->identifier);
-      
+
       if (pident) {
-       newusername = silc_calloc(strlen(username) + 
+       newusername = silc_calloc(strlen(username) +
                                  strlen(pident->host) + 2,
                                  sizeof(*newusername));
        strncat(newusername, username, strlen(username));
@@ -1659,17 +2381,43 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   }
 
   /* Create Client ID */
-  while (!silc_id_create_client_id(server, server->id, server->rng, 
+  while (!silc_id_create_client_id(server, server->id, server->rng,
                                   server->md5hash, nickname, &client_id)) {
     nickfail++;
+    if (nickfail > 9) {
+      silc_server_disconnect_remote(server, sock,
+                                   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);
   }
 
+  /* If client marked as anonymous, scramble the username and hostname */
+  if (client->mode & SILC_UMODE_ANONYMOUS) {
+    char *scramble;
+
+    if (strlen(username) >= 2) {
+      username[0] = silc_rng_get_byte_fast(server->rng);
+      username[1] = silc_rng_get_byte_fast(server->rng);
+    }
+
+    scramble = silc_hash_babbleprint(server->sha1hash, username,
+                                    strlen(username));
+    scramble[5] = '@';
+    scramble[11] = '.';
+    memcpy(&scramble[16], ".silc", 5);
+    scramble[21] = '\0';
+    silc_free(username);
+    username = scramble;
+  }
+
   /* Update client entry */
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
   client->nickname = nickname;
   client->username = username;
-  client->userinfo = realname ? realname : strdup(" ");
+  client->userinfo = realname ? realname : strdup(username);
   client->id = client_id;
   id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
 
@@ -1678,86 +2426,35 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                   client_id, client, 0, NULL);
 
   /* Notify our router about new client on the SILC network */
-  if (!server->standalone)
-    silc_server_send_new_id(server, (SilcSocketConnection) 
-                           server->router->connection, 
-                           server->server_type == SILC_ROUTER ? TRUE : FALSE,
-                           client->id, SILC_ID_CLIENT, id_len);
-  
-  /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
-  reply = silc_buffer_alloc(2 + 2 + id_len);
-  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
-  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, SILC_PRIMARY_ROUTE(server),
+                         SILC_BROADCAST(server), client->id,
+                         SILC_ID_CLIENT, id_len);
 
-  /* Send some nice info to the client */
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Welcome to the SILC Network %s",
-                          username));
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your host is %s, running version %s",
-                          server->config->server_info->server_name,
-                          server_version));
+  /* Distribute to backup routers */
   if (server->server_type == SILC_ROUTER) {
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("There are %d clients on %d servers in SILC "
-                            "Network", server->stat.clients,
-                            server->stat.servers + 1));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("There are %d clients on %d server in our cell",
-                            server->stat.cell_clients,
-                            server->stat.cell_servers + 1));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("I have %d clients, %d channels, %d servers and "
-                            "%d routers",
-                            server->stat.my_clients, 
-                            server->stat.my_channels,
-                            server->stat.my_servers,
-                            server->stat.my_routers));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("There are %d server operators and %d router "
-                            "operators online",
-                            server->stat.server_ops,
-                            server->stat.router_ops));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("I have %d operators online",
-                            server->stat.my_router_ops +
-                            server->stat.my_server_ops));
-  } else {
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("I have %d clients and %d channels formed",
-                            server->stat.my_clients,
-                            server->stat.my_channels));
-    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                           ("%d operators online",
-                            server->stat.my_server_ops));
+    SilcBuffer idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    silc_server_backup_send(server, sock->user_data, SILC_PACKET_NEW_ID, 0,
+                           idp->data, idp->len, FALSE, TRUE);
+    silc_buffer_free(idp);
   }
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your connection is secured with %s cipher, "
-                          "key length %d bits",
-                          idata->send_key->cipher->name,
-                          idata->send_key->cipher->key_len));
-  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
-                         ("Your current nickname is %s",
-                          client->nickname));
-
-  /* Send motd */
-  silc_server_send_motd(server, sock);
+
+  /* Send the new client ID to the client. */
+  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;
 }
 
 /* Create new server. This processes received New Server packet and
    saves the received Server ID. The server is our locally connected
-   server thus we save all the information and save it to local list. 
+   server thus we save all the information and save it to local list.
    This funtion can be used by both normal server and router server.
    If normal server uses this it means that its router has connected
    to the server. If router uses this it means that one of the cell's
@@ -1772,7 +2469,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   SilcServerID *server_id;
   SilcIDListData idata;
   unsigned char *server_name, *id_string;
-  uint16 id_len, name_len;
+  SilcUInt16 id_len, name_len;
   int ret;
   bool local = TRUE;
 
@@ -1788,27 +2485,55 @@ SilcServerEntry silc_server_new_server(SilcServer server,
 
   /* Remove the old cache entry */
   if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) {
-    silc_idcache_del_by_context(server->global_list->servers, new_server);
+    if (!silc_idcache_del_by_context(server->global_list->servers,
+                                    new_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,
+                                   SILC_STATUS_ERR_NOT_AUTHENTICATED, NULL);
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      return NULL;
+    }
     local = FALSE;
   }
 
+  /* Make sure this server hasn't registered already */
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED) {
+    silc_server_disconnect_remote(server, sock,
+                                 SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                 "Too many registrations");
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+    return NULL;
+  }
+
   /* Parse the incoming packet */
   ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&server_name, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&server_name,
                                                         &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;
   }
 
@@ -1820,46 +2545,91 @@ 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);
 
+  /* Check for valid server ID */
+  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,
+                                 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;
+  }
+
   /* Check that we do not have this ID already */
-  server_entry = silc_idlist_find_server_by_id(server->local_list, 
+  server_entry = silc_idlist_find_server_by_id(server->local_list,
                                               server_id, TRUE, NULL);
   if (server_entry) {
-    silc_idcache_del_by_context(server->local_list->servers, server_entry);
+    if (SILC_IS_LOCAL(server_entry)) {
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                   "Too many registrations");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      return NULL;
+    } else {
+      silc_idcache_del_by_context(server->local_list->servers, server_entry);
+    }
   } else {
-    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+    server_entry = silc_idlist_find_server_by_id(server->global_list,
                                                 server_id, TRUE, NULL);
-    if (server_entry) 
-      silc_idcache_del_by_context(server->global_list->servers, server_entry);
+    if (server_entry) {
+      if (SILC_IS_LOCAL(server_entry)) {
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_OPERATION_ALLOWED,
+                                     "Too many registrations");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       return NULL;
+      } else {
+       silc_idcache_del_by_context(server->global_list->servers,
+                                   server_entry);
+      }
+    }
   }
 
   /* Update server entry */
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
   new_server->server_name = server_name;
   new_server->id = server_id;
-  
+
   SILC_LOG_DEBUG(("New server id(%s)",
                  silc_id_render(server_id, SILC_ID_SERVER)));
 
   /* Add again the entry to the ID cache. */
-  silc_idcache_add(local ? server->local_list->servers : 
-                  server->global_list->servers, server_name, server_id, 
+  silc_idcache_add(local ? server->local_list->servers :
+                  server->global_list->servers, server_name, server_id,
                   new_server, 0, NULL);
 
   /* Distribute the information about new server in the SILC network
      to our router. If we are normal server we won't send anything
      since this connection must be our router connection. */
   if (server->server_type == SILC_ROUTER && !server->standalone &&
-      server->router->connection != sock)
-    silc_server_send_new_id(server, server->router->connection,
-                           TRUE, new_server->id, SILC_ID_SERVER, 
+      SILC_PRIMARY_ROUTE(server) != sock)
+    silc_server_send_new_id(server, SILC_PRIMARY_ROUTE(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, sock->user_data, 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
@@ -1867,30 +2637,54 @@ SilcServerEntry silc_server_new_server(SilcServer server,
      protocol has been completed. */
   if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
       silc_server_backup_replaced_get(server, server_id, NULL)) {
-    /* Send packet to the server indicating that it cannot use this
+    /* Send packet to the router indicating that it cannot use this
        connection as it has been replaced by backup router. */
-    SilcBuffer packet = silc_buffer_alloc(2);
-    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-    silc_buffer_format(packet,
-                      SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_REPLACED),
-                      SILC_STR_UI_CHAR(0),
-                      SILC_STR_END);
-    silc_server_packet_send(server, sock, 
-                           SILC_PACKET_RESUME_ROUTER, 0, 
-                           packet->data, packet->len, TRUE);
-    silc_buffer_free(packet);
+    SILC_LOG_DEBUG(("Remote router has been replaced by backup router, "
+                   "disabling its connection"));
+
+    silc_server_backup_send_replaced(server, sock);
 
     /* Mark the router disabled. The data sent earlier will go but nothing
-       after this does not go to this connection. */
+       after this goes to this connection. */
     idata->status |= SILC_IDLIST_STATUS_DISABLED;
   } else {
     /* If it is router announce our stuff to it. */
-    if (sock->type == SILC_SOCKET_TYPE_ROUTER && 
+    if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
        server->server_type == SILC_ROUTER) {
       silc_server_announce_servers(server, FALSE, 0, sock);
       silc_server_announce_clients(server, 0, sock);
       silc_server_announce_channels(server, 0, sock);
     }
+
+    /* Announce our information to backup router */
+    if (new_server->server_type == SILC_BACKUP_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_SERVER &&
+       server->server_type == SILC_ROUTER) {
+      silc_server_announce_servers(server, TRUE, 0, sock);
+      silc_server_announce_clients(server, 0, sock);
+      silc_server_announce_channels(server, 0, sock);
+    }
+
+    /* If backup router, mark it as one of ours.  This server is considered
+       to be backup router after this setting. */
+    if (new_server->server_type == SILC_BACKUP_ROUTER) {
+      SilcServerConfigRouter *backup;
+      backup = silc_server_config_find_backup_conn(server, sock->ip);
+      if (!backup)
+       backup = silc_server_config_find_backup_conn(server, sock->hostname);
+      if (backup) {
+       /* Add as our backup router */
+       silc_server_backup_add(server, new_server, backup->backup_replace_ip,
+                              backup->backup_replace_port,
+                              backup->backup_local);
+      }
+    }
+
+    /* By default the servers connected to backup router are disabled
+       until backup router has become the primary */
+    if (server->server_type == SILC_BACKUP_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_SERVER)
+      idata->status |= SILC_IDLIST_STATUS_DISABLED;
   }
 
   return new_server;
@@ -1899,7 +2693,7 @@ SilcServerEntry silc_server_new_server(SilcServer server,
 /* Processes incoming New ID packet. New ID Payload is used to distribute
    information about newly registered clients and servers. */
 
-static void silc_server_new_id_real(SilcServer server, 
+static void silc_server_new_id_real(SilcServer server,
                                    SilcSocketConnection sock,
                                    SilcPacketContext *packet,
                                    int broadcast)
@@ -1945,11 +2739,11 @@ static void silc_server_new_id_real(SilcServer server,
 
     /* If the sender is backup router and ID is server (and we are not
        backup router) then switch the entry to global list. */
-    if (server_entry->server_type == SILC_BACKUP_ROUTER && 
-       id_type == SILC_ID_SERVER && 
+    if (server_entry->server_type == SILC_BACKUP_ROUTER &&
+       id_type == SILC_ID_SERVER &&
        server->id_entry->server_type != SILC_BACKUP_ROUTER) {
       id_list = server->global_list;
-      router_sock = server->router ? server->router->connection : sock;
+      router_sock = server->router ? SILC_PRIMARY_ROUTE(server) : sock;
     }
   } else {
     void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
@@ -1973,11 +2767,11 @@ static void silc_server_new_id_real(SilcServer server,
       SilcClientEntry entry;
 
       /* Check that we do not have this client already */
-      entry = silc_idlist_find_client_by_id(server->global_list, 
-                                           id, server->server_type, 
+      entry = silc_idlist_find_client_by_id(server->global_list,
+                                           id, server->server_type,
                                            NULL);
       if (!entry)
-       entry = silc_idlist_find_client_by_id(server->local_list, 
+       entry = silc_idlist_find_client_by_id(server->local_list,
                                              id, server->server_type,
                                              NULL);
       if (entry) {
@@ -1989,11 +2783,11 @@ static void silc_server_new_id_real(SilcServer server,
                      silc_id_render(id, SILC_ID_CLIENT),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
                      "Server" : "Router", sock->hostname));
-    
+
       /* As a router we keep information of all global information in our
         global list. Cell wide information however is kept in the local
         list. */
-      entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
+      entry = silc_idlist_add_client(id_list, NULL, NULL, NULL,
                                     id, router, NULL, 0);
       if (!entry) {
        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
@@ -2008,6 +2802,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;
 
@@ -2026,13 +2824,13 @@ static void silc_server_new_id_real(SilcServer server,
        SILC_LOG_DEBUG(("Ignoring sender's own ID"));
        break;
       }
-      
+
       /* Check that we do not have this server already */
-      entry = silc_idlist_find_server_by_id(server->global_list, 
-                                           id, server->server_type, 
+      entry = silc_idlist_find_server_by_id(server->global_list,
+                                           id, server->server_type,
                                            NULL);
       if (!entry)
-       entry = silc_idlist_find_server_by_id(server->local_list, 
+       entry = silc_idlist_find_server_by_id(server->local_list,
                                              id, server->server_type,
                                              NULL);
       if (entry) {
@@ -2044,18 +2842,18 @@ static void silc_server_new_id_real(SilcServer server,
                      silc_id_render(id, SILC_ID_SERVER),
                      sock->type == SILC_SOCKET_TYPE_SERVER ?
                      "Server" : "Router", sock->hostname));
-      
-      /* As a router we keep information of all global information in our 
+
+      /* As a router we keep information of all global information in our
         global list. Cell wide information however is kept in the local
         list. */
-      entry = silc_idlist_add_server(id_list, NULL, 0, id, router, 
+      entry = silc_idlist_add_server(id_list, NULL, 0, id, router,
                                     router_sock);
       if (!entry) {
        SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
        goto out;
       }
       entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
-      
+
       if (sock->type == SILC_SOCKET_TYPE_SERVER)
        server->stat.cell_servers++;
       server->stat.servers++;
@@ -2074,17 +2872,17 @@ 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, 
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           packet->type,
                            packet->flags | SILC_PACKET_FLAG_BROADCAST,
                            buffer->data, buffer->len, FALSE);
-    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+    silc_server_backup_send(server, sock->user_data,
                            packet->type, packet->flags,
-                           packet->buffer->data, packet->buffer->len, 
+                           packet->buffer->data, packet->buffer->len,
                            FALSE, TRUE);
   }
 
@@ -2110,7 +2908,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
 {
   SilcPacketContext *new_id;
   SilcBuffer idp;
-  uint16 id_len;
+  SilcUInt16 id_len;
 
   SILC_LOG_DEBUG(("Processing New ID List"));
 
@@ -2121,17 +2919,18 @@ 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, 
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           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->buffer->data,
+                           packet->buffer->len, FALSE);
+    silc_server_backup_send(server, sock->user_data,
                            packet->type, packet->flags,
-                           packet->buffer->data, packet->buffer->len, 
+                           packet->buffer->data, packet->buffer->len,
                            FALSE, TRUE);
   }
 
@@ -2139,7 +2938,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
      data buffer, which we will here now fetch from the original buffer. */
   new_id = silc_packet_context_alloc();
   new_id->type = SILC_PACKET_NEW_ID;
-  new_id->flags = packet->flags;
+  new_id->flags = packet->flags & (~SILC_PACKET_FLAG_LIST);
   new_id->src_id = packet->src_id;
   new_id->src_id_len = packet->src_id_len;
   new_id->src_id_type = packet->src_id_type;
@@ -2170,7 +2969,7 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
   silc_free(new_id);
 }
 
-/* Received New Channel packet. Information about new channels in the 
+/* Received New Channel packet. Information about new channels in the
    network are distributed using this packet. Save the information about
    the new channel. This usually comes from router but also normal server
    can send this to notify channels it has when it connects to us. */
@@ -2182,14 +2981,12 @@ void silc_server_new_channel(SilcServer server,
   SilcChannelPayload payload;
   SilcChannelID *channel_id;
   char *channel_name;
-  uint32 name_len;
+  SilcUInt32 name_len;
   unsigned char *id;
-  uint32 id_len;
-  uint32 mode;
+  SilcUInt32 id_len, cipher_len;
   SilcServerEntry server_entry;
   SilcChannelEntry channel;
-
-  SILC_LOG_DEBUG(("Processing New Channel"));
+  const char *cipher;
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       packet->src_id_type != SILC_ID_SERVER ||
@@ -2201,7 +2998,7 @@ void silc_server_new_channel(SilcServer server,
                                       packet->buffer->len);
   if (!payload)
     return;
-    
+
   /* Get the channel ID */
   channel_id = silc_channel_get_id_parse(payload);
   if (!channel_id) {
@@ -2218,23 +3015,33 @@ void silc_server_new_channel(SilcServer server,
   server_entry = (SilcServerEntry)sock->user_data;
 
   if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
-    /* Add the channel to global list as it is coming from router. It 
+    /* Add the channel to global list as it is coming from router. It
        cannot be our own channel as it is coming from router. */
 
     /* Check that we don't already have this channel */
-    channel = silc_idlist_find_channel_by_name(server->local_list, 
+    channel = silc_idlist_find_channel_by_name(server->local_list,
                                               channel_name, NULL);
     if (!channel)
-      channel = silc_idlist_find_channel_by_name(server->global_list, 
+      channel = silc_idlist_find_channel_by_name(server->global_list,
                                                 channel_name, NULL);
     if (!channel) {
       SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
-                     silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                     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) {
+       silc_channel_payload_free(payload);
+       silc_free(channel_id);
+       return;
+      }
+      channel->disabled = TRUE;    /* Disabled until someone JOINs */
+
       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
@@ -2242,19 +3049,21 @@ void silc_server_new_channel(SilcServer server,
     SilcBuffer chk;
 
     SILC_LOG_DEBUG(("Channel id(%s) from [Server] %s",
-                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                   silc_id_render(channel_id, SILC_ID_CHANNEL),
                    sock->hostname));
 
     /* Check that we don't already have this channel */
-    channel = silc_idlist_find_channel_by_name(server->local_list, 
+    channel = silc_idlist_find_channel_by_name(server->local_list,
                                               channel_name, NULL);
     if (!channel)
-      channel = silc_idlist_find_channel_by_name(server->global_list, 
+      channel = silc_idlist_find_channel_by_name(server->global_list,
                                                 channel_name, NULL);
 
     /* If the channel does not exist, then create it. This creates a new
        key to the channel as well that we will send to the server. */
     if (!channel) {
+      SILC_LOG_DEBUG(("Channel is new to us"));
+
       /* The protocol says that the Channel ID's IP address must be based
         on the router's IP address.  Check whether the ID is based in our
         IP and if it is not then create a new ID and enforce the server
@@ -2263,13 +3072,16 @@ void silc_server_new_channel(SilcServer server,
          !SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) {
        SilcChannelID *tmp;
        SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
-       
        if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) {
-         silc_server_send_notify_channel_change(server, sock, FALSE, 
+         silc_server_send_notify_channel_change(server, sock, FALSE,
                                                 channel_id, tmp);
+         silc_channel_payload_free(payload);
          silc_free(channel_id);
-         channel_id = tmp;
+         silc_free(tmp);
        }
+
+       /* Wait that server re-announces this channel */
+       return;
       }
 
       /* Create the channel with the provided Channel ID */
@@ -2281,37 +3093,54 @@ void silc_server_new_channel(SilcServer server,
        silc_free(channel_id);
        return;
       }
+      channel->disabled = TRUE;    /* Disabled until someone JOINs */
+
+#if 0 /* We assume that CMODE_CHANGE notify is sent to us after this. */
 
-      /* Get the mode and set it to the channel */
-      channel->mode = silc_channel_get_mode(payload);
+      /* XXX Dunno if this is supposed to be set in any server type.  If set
+        here the CMODE_CHANGE that may follow sets mode that we already
+        have, and we may loose data from the CMODE_CHANGE notify. */
+      if (server_entry->server_type != SILC_BACKUP_ROUTER)
+       channel->mode = silc_channel_get_mode(payload);
+#endif
 
       /* Send the new channel key to the server */
       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+      cipher = silc_cipher_get_name(channel->channel_key);
+      cipher_len = strlen(cipher);
       chk = silc_channel_key_payload_encode(id_len, id,
-                                           strlen(channel->channel_key->
-                                                  cipher->name),
-                                           channel->channel_key->cipher->name,
-                                           channel->key_len / 8, 
+                                           cipher_len, cipher,
+                                           channel->key_len / 8,
                                            channel->key);
-      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
                              chk->data, chk->len, FALSE);
       silc_buffer_free(chk);
-
+      silc_free(id);
     } 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;
+
+      SILC_LOG_DEBUG(("Channel already exists"));
 
       if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
           force the ID change. */
        SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
-       silc_server_send_notify_channel_change(server, sock, FALSE, 
+       silc_server_send_notify_channel_change(server, sock, FALSE,
                                               channel_id, channel->id);
+       silc_channel_payload_free(payload);
+       silc_free(channel_id);
+
+       /* Wait that server re-announces this channel */
+       return;
       }
 
+#if 0 /* We will announce our CMODE anyway for this channel, so no need
+        to check it (implicit enforce). */
+
       /* If the mode is different from what we have then enforce the
         mode change. */
       mode = silc_channel_get_mode(payload);
@@ -2319,32 +3148,38 @@ 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;
-       
-       /* Send to the channel */
-       silc_server_send_channel_key(server, sock, channel, FALSE);
-       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+
+       if (silc_hash_table_count(channel->user_list)) {
+         if (!silc_server_create_channel_key(server, channel, 0)) {
+           silc_channel_payload_free(payload);
+           silc_free(channel_id);
+           return;
+         }
+
+         /* Send to the channel */
+         silc_server_send_channel_key(server, sock, channel, FALSE);
+       }
 
        /* Send to the server */
+       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+       cipher = silc_cipher_get_name(channel->channel_key);
+       cipher_len = strlen(cipher);
        chk = silc_channel_key_payload_encode(id_len, id,
-                                             strlen(channel->channel_key->
-                                                    cipher->name),
-                                             channel->channel_key->
-                                             cipher->name,
-                                             channel->key_len / 8, 
+                                             cipher_len, cipher,
+                                             channel->key_len / 8,
                                              channel->key);
-       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
                                chk->data, chk->len, FALSE);
        silc_buffer_free(chk);
        silc_free(id);
@@ -2355,7 +3190,7 @@ void silc_server_new_channel(SilcServer server,
       /* 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 (users) {
        silc_buffer_push(users, users->data - users->head);
@@ -2364,18 +3199,51 @@ void silc_server_new_channel(SilcServer server,
                                users->data, users->len, FALSE);
        silc_buffer_free(users);
       }
+      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_modes) {
        silc_buffer_push(users_modes, users_modes->data - users_modes->head);
        silc_server_packet_send_dest(server, sock,
                                     SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
                                     channel->id, SILC_ID_CHANNEL,
-                                    users_modes->data, 
+                                    users_modes->data,
                                     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);
+      }
     }
   }
 
+  /* 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->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New Channel packet"));
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           packet->type,
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data,
+                           packet->buffer->len, FALSE);
+    silc_server_backup_send(server, sock->user_data,
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len,
+                           FALSE, TRUE);
+  }
+
   silc_channel_payload_free(payload);
 }
 
@@ -2388,7 +3256,7 @@ void silc_server_new_channel_list(SilcServer server,
 {
   SilcPacketContext *new;
   SilcBuffer buffer;
-  uint16 len1, len2;
+  SilcUInt16 len1, len2;
 
   SILC_LOG_DEBUG(("Processing New Channel List"));
 
@@ -2397,28 +3265,11 @@ void silc_server_new_channel_list(SilcServer server,
       server->server_type == SILC_SERVER)
     return;
 
-  /* 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 &&
-      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);
-    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
-                           packet->type, packet->flags,
-                           packet->buffer->data, packet->buffer->len, 
-                           FALSE, TRUE);
-  }
-
   /* Make copy of the original packet context, except for the actual
      data buffer, which we will here now fetch from the original buffer. */
   new = silc_packet_context_alloc();
   new->type = SILC_PACKET_NEW_CHANNEL;
-  new->flags = packet->flags;
+  new->flags = packet->flags & (~SILC_PACKET_FLAG_LIST);
   new->src_id = packet->src_id;
   new->src_id_len = packet->src_id_len;
   new->src_id_type = packet->src_id_type;
@@ -2477,7 +3328,8 @@ void silc_server_key_agreement(SilcServer server,
 
   /* Get the route to the client */
   dst_sock = silc_server_get_client_route(server, packet->dst_id,
-                                         packet->dst_id_len, NULL, &idata);
+                                         packet->dst_id_len, NULL,
+                                         &idata, NULL);
   if (!dst_sock)
     return;
 
@@ -2499,15 +3351,15 @@ void silc_server_connection_auth_request(SilcServer server,
                                         SilcSocketConnection sock,
                                         SilcPacketContext *packet)
 {
-  SilcServerConfigSectionClient *client = NULL;
-  uint16 conn_type;
-  int ret, port;
+  SilcServerConfigClient *client = NULL;
+  SilcUInt16 conn_type;
+  int ret;
   SilcAuthMethod auth_meth = SILC_AUTH_NONE;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT)
+  if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT) {
+    SILC_LOG_DEBUG(("Request not from client"));
     return;
+  }
 
   /* Parse the payload */
   ret = silc_buffer_unformat(packet->buffer,
@@ -2522,20 +3374,24 @@ void silc_server_connection_auth_request(SilcServer server,
 
   /* Get the authentication method for the client */
   auth_meth = SILC_AUTH_NONE;
-  port = server->sockets[server->sock]->port; /* Listenning port */
-  client = silc_server_config_find_client(server, sock->ip, port);
+  client = silc_server_config_find_client(server, sock->ip);
   if (!client)
-    client = silc_server_config_find_client(server, sock->hostname, port);
+    client = silc_server_config_find_client(server, sock->hostname);
   if (client) {
     if (client->passphrase) {
-      if (client->publickey && !server->config->prefer_passphrase_auth)
+      if (client->publickeys && !server->config->prefer_passphrase_auth)
        auth_meth = SILC_AUTH_PUBLIC_KEY;
       else
        auth_meth = SILC_AUTH_PASSWORD;
-    } else if (client->publickey)
+    } else if (client->publickeys)
       auth_meth = SILC_AUTH_PUBLIC_KEY;
   }
 
+  SILC_LOG_DEBUG(("Authentication method is [%s]",
+                 (auth_meth == SILC_AUTH_NONE ? "None" :
+                  auth_meth == SILC_AUTH_PASSWORD ? "Passphrase" :
+                  "Digital signatures")));
+
   /* Send it back to the client */
   silc_server_send_connection_auth_request(server, sock, conn_type, auth_meth);
 }
@@ -2551,19 +3407,28 @@ void silc_server_rekey(SilcServer server,
   SilcServerRekeyInternalContext *proto_ctx;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Received rekey request"));
+
+  /* If we have other protocol executing we have no other choice but to
+     not execute rekey. XXX This is very bad thing.  Let's hope this
+     doesn't happen often. */
+  if (sock->protocol) {
+    SILC_LOG_WARNING(("Cannot execute REKEY protocol because other protocol "
+                     "is executing at the same time"));
+    return;
+  }
 
   /* Allocate internal protocol context. This is sent as context
      to the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock;
+  proto_ctx->sock = silc_socket_dup(sock);
   proto_ctx->responder = TRUE;
   proto_ctx->pfs = idata->rekey->pfs;
-      
+
   /* Perform rekey protocol. Will call the final callback after the
      protocol is over. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY,
                      &protocol, proto_ctx, silc_server_rekey_final);
   sock->protocol = protocol;
 
@@ -2595,7 +3460,8 @@ void silc_server_ftp(SilcServer server,
 
   /* Get the route to the client */
   dst_sock = silc_server_get_client_route(server, packet->dst_id,
-                                         packet->dst_id_len, NULL, &idata);
+                                         packet->dst_id_len, NULL,
+                                         &idata, NULL);
   if (!dst_sock)
     return;
 
@@ -2604,3 +3470,591 @@ void silc_server_ftp(SilcServer server,
                           idata->hmac_send, idata->psn_send++,
                           packet, FALSE);
 }
+
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  SilcPacketContext *packet;
+  void *data;
+} *SilcServerResumeResolve;
+
+SILC_SERVER_CMD_FUNC(resume_resolve)
+{
+  SilcServerResumeResolve r = (SilcServerResumeResolve)context;
+  SilcServer server = r->server;
+  SilcSocketConnection sock = r->sock;
+  SilcServerCommandReplyContext reply = context2;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  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,
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 "Resuming not possible");
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+    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");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       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");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      goto out;
+    }
+
+    client->data.status |= SILC_IDLIST_STATUS_RESUME_RES;
+  }
+
+  /* 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);
+}
+
+/* Received client resuming packet.  This is used to resume detached
+   client session.  It can be sent by the client who wishes to resume
+   but this is also sent by servers and routers to notify other routers
+   that the client is not detached anymore. */
+
+void silc_server_resume_client(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer, buf;
+  SilcIDListData idata;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry detached_client;
+  SilcClientID *client_id = NULL;
+  unsigned char *id_string, *auth = NULL;
+  SilcUInt16 id_len, auth_len = 0;
+  int ret, nickfail = 0;
+  bool resolved, local, nick_change = FALSE, resolve = FALSE;
+  SilcChannelEntry channel;
+  SilcHashTableList htl;
+  SilcChannelClientEntry chl;
+  SilcServerResumeResolve r;
+  const char *cipher;
+
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING(&id_string, &id_len),
+                            SILC_STR_END);
+  if (ret != -1)
+    client_id = silc_id_str2id(id_string, id_len, SILC_ID_CLIENT);
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    /* Client send this and is attempting to resume to old client session */
+    SilcClientEntry client;
+    SilcBuffer keyp;
+
+    if (ret != -1) {
+      silc_buffer_pull(buffer, 2 + id_len);
+      auth = buffer->data;
+      auth_len = buffer->len;
+      silc_buffer_push(buffer, 2 + id_len);
+    }
+
+    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,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_free(client_id);
+      return;
+    }
+
+    /* Take client entry of this connection */
+    client = (SilcClientEntry)sock->user_data;
+    idata = (SilcIDListData)client;
+
+    /* Get entry to the client, and resolve it if we don't have it. */
+    detached_client = silc_server_query_client(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 = client_id;
+       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,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_free(client_id);
+      }
+      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 (detached_client->data.status & SILC_IDLIST_STATUS_RESUME_RES)
+      resolve = FALSE;
+
+    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_query_client(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 = client_id;
+       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");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_free(client_id);
+       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->server_type == SILC_SERVER && server->standalone) {
+       SILC_LOG_ERROR(("Detached client's public key not present, "
+                       "closing connection"));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_free(client_id);
+      } else {
+       /* We must retrieve the detached client's public key by sending
+          GETKEY command. Reprocess this packet after receiving the key */
+       SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+       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 :
+                                SILC_PRIMARY_ROUTE(server),
+                                SILC_COMMAND_GETKEY, ++server->cmd_ident,
+                                1, 1, idp->data, idp->len);
+
+       r = silc_calloc(1, sizeof(*r));
+       if (!r) {
+         silc_free(client_id);
+         return;
+       }
+
+       r->server = server;
+       r->sock = silc_socket_dup(sock);
+       r->packet = silc_packet_context_dup(packet);
+       silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+                                   server->cmd_ident,
+                                   silc_server_command_resume_resolve, r);
+
+       silc_buffer_free(idp);
+      }
+      silc_free(client_id);
+      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_LOG_ERROR(("Resuming attempted with wrong public key, "
+                     "closing connection"));
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_free(client_id);
+      return;
+    }
+
+    /* Verify the authentication payload.  This has to be successful in
+       order to allow the resuming */
+    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,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_free(client_id);
+      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;
+
+    /* Take new keys and stuff into use in the old entry */
+    silc_idlist_del_data(detached_client);
+    silc_idlist_add_data(detached_client, idata);
+    detached_client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+    detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+    detached_client->data.status |= SILC_IDLIST_STATUS_LOCAL;
+    detached_client->data.status &= ~SILC_IDLIST_STATUS_RESUME_RES;
+    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. */
+    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 */
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           SILC_PACKET_RESUME_CLIENT, 0,
+                           buf->data, buf->len, TRUE);
+    silc_server_backup_send(server, detached_client->router,
+                           SILC_PACKET_RESUME_CLIENT, 0,
+                           buf->data, buf->len, TRUE, TRUE);
+
+    /* 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_remove_from_channels(server, NULL, client, FALSE,
+                                    NULL, FALSE, FALSE);
+    silc_server_del_from_watcher_list(server, client);
+    if (!silc_idlist_del_client(server->local_list, client))
+      silc_idlist_del_client(server->global_list, client);
+    client = detached_client;
+    silc_free(client->servername);
+    client->servername = strdup(server->server_name);
+
+    /* If the ID is not based in our ID then change it */
+    if (!SILC_ID_COMPARE(client->id, server->id, server->id->ip.data_len)) {
+      silc_free(client_id);
+      while (!silc_id_create_client_id(server, server->id, server->rng,
+                                      server->md5hash, client->nickname,
+                                      &client_id)) {
+       nickfail++;
+       if (nickfail > 9) {
+         silc_server_disconnect_remote(server, sock,
+                                       SILC_STATUS_ERR_BAD_NICKNAME, NULL);
+         if (sock->user_data)
+           silc_server_free_sock_user_data(server, sock, 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. */
+      silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
+                                         SILC_BROADCAST(server),
+                                         client->id, client_id,
+                                         client->nickname);
+    }
+
+    /* 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, SILC_PRIMARY_ROUTE(server),
+                                  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, 0, NULL);
+
+    /* Send some nice info to the client */
+    silc_server_send_connect_notifys(server, sock, client);
+
+    /* 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);
+      cipher = silc_cipher_get_name(channel->channel_key);
+      keyp =
+       silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+                                                       SILC_ID_CHANNEL),
+                                       id_string,
+                                       strlen(cipher), cipher,
+                                       channel->key_len / 8, channel->key);
+      silc_free(id_string);
+
+      /* Send the channel key to the client */
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
+                             keyp->data, keyp->len, FALSE);
+
+      /* Distribute the channel key to channel */
+      if (created) {
+       silc_server_send_channel_key(server, NULL, channel,
+                                    server->server_type == SILC_ROUTER ?
+                                    FALSE : !server->standalone);
+       silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
+                               keyp->data, keyp->len, FALSE, TRUE);
+      }
+
+      silc_buffer_free(keyp);
+    }
+    silc_hash_table_list_reset(&htl);
+
+  } else if (sock->type != SILC_SOCKET_TYPE_CLIENT) {
+    /* Server or router sent this to us to notify that that a client has
+       been resumed. */
+    SilcServerEntry server_entry;
+    SilcServerID *server_id;
+
+    if (!client_id) {
+      SILC_LOG_DEBUG(("Malformed resuming packet"));
+      return;
+    }
+
+    /* Get entry to the client, and resolve it if we don't have it. */
+    detached_client = silc_idlist_find_client_by_id(server->local_list,
+                                                   client_id, TRUE,
+                                                   &id_cache);
+    if (!detached_client) {
+      detached_client = silc_idlist_find_client_by_id(server->global_list,
+                                                     client_id, TRUE,
+                                                     &id_cache);
+      if (!detached_client) {
+       SILC_LOG_DEBUG(("Resuming client is unknown"));
+       silc_free(client_id);
+       return;
+      }
+    }
+
+    /* Check that the client has not been resumed already because it is
+       protocol error to attempt to resume more than once.  The client
+       will be killed if this protocol error occurs. */
+    if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED &&
+       !(detached_client->mode & SILC_UMODE_DETACHED)) {
+      /* The client is clearly attempting to resume more than once and
+        perhaps playing around by resuming from several different places
+        at the same time. */
+      SILC_LOG_DEBUG(("Attempting to re-resume client, killing both"));
+      silc_server_kill_client(server, detached_client, NULL,
+                             server->id, SILC_ID_SERVER);
+      silc_free(client_id);
+      return;
+    }
+
+    /* Check whether client is detached at all */
+    if (!(detached_client->mode & SILC_UMODE_DETACHED)) {
+      SILC_LOG_DEBUG(("Client is not detached"));
+      silc_free(client_id);
+      return;
+    }
+
+    SILC_LOG_DEBUG(("Resuming 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->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, SILC_PRIMARY_ROUTE(server),
+                             packet->type,
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             buffer->data, buffer->len, FALSE);
+      silc_server_backup_send(server, sock->user_data,
+                             packet->type, packet->flags,
+                             packet->buffer->data, packet->buffer->len,
+                             FALSE, TRUE);
+    }
+
+    /* Client is detached, and now it is resumed.  Remove the detached
+       mode and mark that it is resumed. */
+    silc_idlist_del_data(detached_client);
+    detached_client->mode &= ~SILC_UMODE_DETACHED;
+    detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+    detached_client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
+    id_cache->expire = 0;
+
+    silc_schedule_task_del_by_context(server->schedule, detached_client);
+
+    /* Get the new owner of the resumed client */
+    server_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                              packet->src_id_type);
+    if (!server_id) {
+      silc_free(client_id);
+      return;
+    }
+
+    /* Get server entry */
+    server_entry = silc_idlist_find_server_by_id(server->global_list,
+                                                server_id, TRUE, NULL);
+    local = FALSE;
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list,
+                                                  server_id, TRUE, NULL);
+      local = TRUE;
+      if (!server_entry) {
+       silc_free(server_id);
+       silc_free(client_id);
+       return;
+      }
+    }
+
+    if (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER &&
+       server_entry->server_type == SILC_ROUTER)
+      local = FALSE;
+
+    /* 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->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 */
+    detached_client->router = server_entry;
+
+    /* Update channel information regarding global clients on channel. */
+    if (server->server_type != SILC_ROUTER) {
+      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_free(server_id);
+  }
+
+  silc_free(client_id);
+}