updates.
[silc.git] / apps / silcd / packet_receive.c
index f0c3616da003fd8061cd92cc3000ec7032e41c8a..03fcde91e8430f7cb9e16d2072bb163e618e7920 100644 (file)
@@ -25,8 +25,6 @@
 #include "serverincludes.h"
 #include "server_internal.h"
 
-extern char *server_version;
-
 /* Received notify packet. Server can receive notify packets from router. 
    Server then relays the notify messages to clients if needed. */
 
@@ -69,7 +67,8 @@ 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,
@@ -331,7 +330,7 @@ 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);
@@ -339,6 +338,11 @@ void silc_server_notify(SilcServer server,
     /* Remove the client from all channels. */
     silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
 
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_SIGNOFF);
+
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
     break;
@@ -403,8 +407,9 @@ void silc_server_notify(SilcServer server,
     /* Get user's channel entry and check that topic set is allowed. */
     if (!silc_server_client_on_channel(client, channel, &chl))
       goto out;
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+    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;
     }
@@ -456,16 +461,18 @@ void silc_server_notify(SilcServer server,
       nickname = silc_argument_get_arg_type(args, 3, &nickname_len);;
 
       /* Replace the Client ID */
-      client = silc_idlist_replace_client_id(server->global_list, client_id,
+      client = silc_idlist_replace_client_id(server,
+                                            server->global_list, client_id,
                                             client_id2, nickname);
       if (!client)
-       client = silc_idlist_replace_client_id(server->local_list, client_id, 
+       client = silc_idlist_replace_client_id(server,
+                                              server->local_list, client_id, 
                                               client_id2, nickname);
 
       if (client) {
        /* Send the NICK_CHANGE notify type to local clients on the channels
           this client is joined to. */
-       silc_server_send_notify_on_channels(server, NULL, client,
+       silc_server_send_notify_on_channels(server, client, client,
                                            SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
                                            id, tmp_len, id2, tmp_len,
                                            nickname, nickname ?
@@ -568,9 +575,6 @@ void silc_server_notify(SilcServer server,
                                   FALSE : !server->standalone);
     }
 
-    /* Change mode */
-    channel->mode = mode;
-
     /* Get the hmac */
     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
     if (tmp) {
@@ -584,8 +588,7 @@ void silc_server_notify(SilcServer server,
       /* Set the HMAC key out of current channel key. The client must do
         this locally. */
       silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
-                    channel->key_len / 8, 
-                    hash);
+                    channel->key_len / 8, hash);
       silc_hmac_set_key(channel->hmac, hash, 
                        silc_hash_len(silc_hmac_get_hash(channel->hmac)));
       memset(hash, 0, sizeof(hash));
@@ -595,7 +598,49 @@ 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;
+      silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+
+      if (!channel->founder_key || 
+         (client && client->data.public_key && 
+          server->server_type == SILC_ROUTER &&
+          !silc_pkcs_public_key_compare(channel->founder_key,
+                                        client->data.public_key))) {
+       /* A really buggy server isn't checking public keys correctly.
+          It's not possible that the mode setter and founder wouldn't
+          have same public key. */
+       SILC_LOG_DEBUG(("Enforcing sender to change channel mode"));
+
+       mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     mode, server->id, SILC_ID_SERVER,
+                                     channel->cipher, 
+                                     channel->hmac_name,
+                                     channel->passphrase, NULL);
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+      } else if (!client->data.public_key) {
+       client->data.public_key = 
+         silc_pkcs_public_key_copy(channel->founder_key);
+      }
+    }
+
+    /* Change mode */
+    channel->mode = mode;
+
+    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) &&
+       channel->founder_key) {
+      silc_pkcs_public_key_free(channel->founder_key);
+      channel->founder_key = NULL;
     }
 
     break;
@@ -689,7 +734,8 @@ void silc_server_notify(SilcServer server,
        
        if (client != client2) {
          /* Sender must be operator */
-         if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+         if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+             !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
            SILC_LOG_DEBUG(("CUMODE change is not allowed"));
            goto out;
          }
@@ -706,66 +752,53 @@ void silc_server_notify(SilcServer server,
        }
       }
 
-      /* Get entry to the channel user list */
-      silc_hash_table_list(channel->user_list, &htl);
-      while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-       /* If the mode is channel founder and we already find a client 
-          to have that mode on the channel we will enforce the sender
-          to change the channel founder mode away. There can be only one
-          channel founder on the channel. */
-       if (server->server_type == SILC_ROUTER &&
-           mode & SILC_CHANNEL_UMODE_CHANFO &&
-           chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
-         SilcBuffer idp;
-         unsigned char cumode[4];
-
-         if (chl->client == client && chl->mode == mode) {
-           notify_sent = TRUE;
-           break;
-         }
-
+      if (mode & SILC_CHANNEL_UMODE_CHANFO &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO) && 
+         server->server_type == SILC_ROUTER) {
+       /* Check whether this client is allowed to be channel founder on
+          this channel. */
+
+       /* If channel doesn't have founder auth mode then it's impossible
+          that someone would be getting founder rights with CUMODE command.
+          In that case there already either is founder or there isn't
+          founder at all on the channel. */
+       if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
+         /* Force the mode to not have founder mode */
          mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-         silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
-                                        client2->id, SILC_ID_CLIENT,
-                                        client2->id);
-         
-         idp = silc_id_payload_encode(client2->id, SILC_ID_CLIENT);
-         SILC_PUT32_MSB(mode, cumode);
-         silc_server_send_notify_to_channel(server, sock, channel, FALSE, 
-                                            SILC_NOTIFY_TYPE_CUMODE_CHANGE,
-                                            3, idp->data, idp->len,
-                                            cumode, 4,
-                                            idp->data, idp->len);
-         silc_buffer_free(idp);
+         silc_server_force_cumode_change(server, sock, channel, chl, mode);
          notify_sent = TRUE;
-
-         /* Force the mode change if we alredy set the mode */
-         if (chl2) {
-           chl2->mode = mode;
-           silc_free(channel_id);
-           silc_hash_table_list_reset(&htl);
-           goto out;
-         }
+         break;
        }
-       
-       if (chl->client == client2) {
-         if (chl->mode == mode) {
+
+       /* Get the founder of the channel and if found then this client
+          cannot be the founder since there already is one. */
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
+         if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
+           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, sock, channel, chl, mode);
            notify_sent = TRUE;
            break;
          }
+       silc_hash_table_list_reset(&htl);
+       if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+         break;
+
+       /* XXX 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 we don't have the public key it
+          is resolved first.
+       if (!silc_pkcs_public_key_compare(channel->founder_key,
+                                         client->data.public_key))
+       */
+      }
 
-         SILC_LOG_DEBUG(("Changing the channel user mode"));
+      SILC_LOG_DEBUG(("Changing the channel user mode"));
+
+      /* Change the mode */
+      chl->mode = mode;
 
-         /* Change the mode */
-         chl->mode = mode;
-         if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
-           break;
-         
-         chl2 = chl;
-       }
-      }
-      silc_hash_table_list_reset(&htl);
-      
       /* Send the same notify to the channel */
       if (!notify_sent)
        silc_server_packet_send_to_channel(server, sock, channel, 
@@ -829,8 +862,9 @@ void silc_server_notify(SilcServer server,
     /* Get user's channel entry and check that inviting is allowed. */
     if (!silc_server_client_on_channel(client, channel, &chl))
       goto out;
-    if (chl->mode == SILC_CHANNEL_UMODE_NONE && 
-       channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Inviting is not allowed"));
       goto out;
     }
@@ -1039,7 +1073,7 @@ 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);
@@ -1048,6 +1082,15 @@ void silc_server_notify(SilcServer server,
            silc_server_remove_from_channels(server, NULL, client, 
                                             TRUE, NULL, FALSE);
 
+           /* Check if anyone is watching this nickname */
+           if (server->server_type == SILC_ROUTER)
+             silc_server_check_watcher_list(server, client, NULL,
+                                            SILC_NOTIFY_TYPE_SERVER_SIGNOFF);
+
+           /* Remove this client from watcher list if it is */
+           if (local)
+             silc_server_del_from_watcher_list(server, client);
+
            /* Remove the client */
            silc_idlist_del_client(local ? server->local_list :
                                   server->global_list, client);
@@ -1149,7 +1192,8 @@ void silc_server_notify(SilcServer server,
       /* Kicker must be operator on channel */
       if (!silc_server_client_on_channel(client2, channel, &chl))
        goto out;
-      if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+      if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+         !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
        SILC_LOG_DEBUG(("Kicking is not allowed"));
        goto out;
       }
@@ -1213,27 +1257,29 @@ void silc_server_notify(SilcServer server,
       /* From protocol version 1.1 we get the killer's ID as well. */
       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
       if (tmp) {
-       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+       client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
        if (!client_id)
          goto out;
 
-       /* 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, 
+       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) {
-           silc_free(client_id);
-           goto out;
+           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);
+         silc_free(client_id);
 
-       /* Killer must be router operator */
-       if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
-         SILC_LOG_DEBUG(("Killing is not allowed"));
-         goto out;
+         /* Killer must be router operator */
+         if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+           SILC_LOG_DEBUG(("Killing is not allowed"));
+           goto out;
+         }
        }
       }
 
@@ -1248,6 +1294,11 @@ void silc_server_notify(SilcServer server,
       silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
                                       FALSE);
 
+      /* Check if anyone is watching this nickname */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_check_watcher_list(server, client, NULL,
+                                      SILC_NOTIFY_TYPE_KILLED);
+
       break;
     }
 
@@ -1291,9 +1342,18 @@ void silc_server_notify(SilcServer server,
       goto out;
     }
 
+    /* Remove internal resumed flag if client is marked detached now */
+    if (mode & SILC_UMODE_DETACHED)
+      client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+
     /* 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:
@@ -1362,6 +1422,42 @@ void silc_server_notify(SilcServer server,
     }
     break;
 
+  case SILC_NOTIFY_TYPE_ERROR:
+    {
+      /*
+       * Error notify
+       */
+      SilcStatus error;
+
+      tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!tmp && tmp_len != 1)
+       goto out;
+      error = (SilcStatus)tmp[0];
+
+      SILC_LOG_DEBUG(("ERROR notify (%d)", error));
+
+      if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
+         sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+       if (tmp) {
+         SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
+                         "the entry from cache"));
+         client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+         if (!client_id)
+           goto out;
+         client = silc_idlist_find_client_by_id(server->global_list, 
+                                                client_id, FALSE, NULL);
+         if (client) {
+           silc_server_remove_from_channels(server, NULL, client, TRUE, 
+                                            NULL, TRUE);
+           silc_idlist_del_client(server->global_list, client);
+         }
+         silc_free(client_id);
+       }
+      }
+    }
+    break;
+
     /* Ignore rest of the notify types for now */
   case SILC_NOTIFY_TYPE_NONE:
   case SILC_NOTIFY_TYPE_MOTD:
@@ -1439,6 +1535,7 @@ void silc_server_private_message(SilcServer server,
 {
   SilcSocketConnection dst_sock;
   SilcIDListData idata;
+  SilcClientEntry client;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1448,36 +1545,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++,
@@ -1507,7 +1622,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;
 
@@ -1621,7 +1737,34 @@ void silc_server_channel_message(SilcServer server,
   if (!channel) {
     channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
     if (!channel) {
-      SILC_LOG_DEBUG(("Could not find channel"));
+      SilcBuffer idp;
+      unsigned char error;
+
+      /* Send SILC_NOTIFY_TYPE_ERROR to indicate that such destination ID
+        does not exist or is invalid. */
+      idp = silc_id_payload_encode_data(packet->dst_id,
+                                       packet->dst_id_len,
+                                       packet->dst_id_type);
+      if (!idp)
+       goto out;
+
+      error = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
+      if (packet->src_id_type == SILC_ID_CLIENT) {
+       SilcClientID *client_id = silc_id_str2id(packet->src_id,
+                                                packet->src_id_len,
+                                                packet->src_id_type);
+       silc_server_send_notify_dest(server, sock, FALSE,
+                                    client_id, SILC_ID_CLIENT,
+                                    SILC_NOTIFY_TYPE_ERROR, 2,
+                                    &error, 1, idp->data, idp->len);
+       silc_free(client_id);
+      } else {
+       silc_server_send_notify(server, sock, FALSE,
+                               SILC_NOTIFY_TYPE_ERROR, 2,
+                               &error, 1, idp->data, idp->len);
+      }
+      
+      silc_buffer_free(idp);
       goto out;
     }
   }
@@ -1648,7 +1791,9 @@ void silc_server_channel_message(SilcServer server,
 
     /* If channel is moderated check that client is allowed to send
        messages. */
-    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chl->mode) {
+    if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && 
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANOP) &&
+       !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
       SILC_LOG_DEBUG(("Channel is silenced from normal users"));
       goto out;
     }
@@ -1724,9 +1869,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   SilcBuffer buffer = packet->buffer;
   SilcClientEntry client;
   SilcClientID *client_id;
-  SilcBuffer reply;
   SilcIDListData idata;
-  char *username = NULL, *realname = NULL, *id_string;
+  char *username = NULL, *realname = NULL;
   SilcUInt16 username_len;
   SilcUInt32 id_len;
   int ret;
@@ -1745,8 +1889,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   /* Remove the old cache entry. */
   if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
     SILC_LOG_INFO(("Unauthenticated client attempted to register to network"));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "You have not been authenticated");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_NOT_AUTHENTICATED, NULL);
     return NULL;
   }
 
@@ -1761,8 +1905,9 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     silc_free(realname);
     SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
                    "connection", sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Incomplete client information");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION, 
+                                 NULL);
     return NULL;
   }
 
@@ -1771,8 +1916,9 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     silc_free(realname);
     SILC_LOG_ERROR(("Client %s (%s) did not send its username, closing "
                    "connection", sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                  "Incomplete client information");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                 NULL);
     return NULL;
   }
 
@@ -1807,8 +1953,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
       SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
                      "connection", sock->hostname, sock->ip));
       silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: "
-                                   "Incomplete client information");
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   NULL);
       return NULL;
     }
     
@@ -1827,8 +1973,8 @@ SilcClientEntry silc_server_new_client(SilcServer server,
       SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
                      "connection", sock->hostname, sock->ip));
       silc_server_disconnect_remote(server, sock, 
-                                   "Server closed connection: "
-                                   "Incomplete client information");
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   NULL);
       return NULL;
     }
     
@@ -1874,6 +2020,11 @@ SilcClientEntry silc_server_new_client(SilcServer server,
   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);
+      return NULL;
+    }
     snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
   }
 
@@ -1897,71 +2048,15 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                            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, 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_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->server_name, server_version));
-  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));
-  }
-  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);
+  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;
 }
@@ -2004,8 +2099,8 @@ SilcServerEntry silc_server_new_server(SilcServer server,
       SILC_LOG_INFO(("Unauthenticated %s attempted to register to "
                     "network", (sock->type == SILC_SOCKET_TYPE_SERVER ?
                                 "server" : "router")));
-      silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                   "You have not been authenticated");
+      silc_server_disconnect_remote(server, sock, 
+                                   SILC_STATUS_ERR_NOT_AUTHENTICATED, NULL);
       return NULL;
     }
     local = FALSE;
@@ -2047,8 +2142,8 @@ SilcServerEntry silc_server_new_server(SilcServer server,
   if (!silc_id_is_valid_server_id(server, server_id, sock)) {
     SILC_LOG_INFO(("Invalid server ID sent by %s (%s)",
                   sock->ip, sock->hostname));
-    silc_server_disconnect_remote(server, sock, "Server closed connection: "
-                                 "Your Server ID is not valid");
+    silc_server_disconnect_remote(server, sock, 
+                                 SILC_STATUS_ERR_BAD_SERVER_ID, NULL);
     silc_free(server_name);
     return NULL;
   }
@@ -2237,6 +2332,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;
 
@@ -2461,9 +2560,15 @@ void silc_server_new_channel(SilcServer server,
                      silc_id_render(channel_id, SILC_ID_CHANNEL), 
                      sock->hostname));
     
-      silc_idlist_add_channel(server->global_list, strdup(channel_name), 
-                             0, channel_id, sock->user_data, NULL, NULL, 0);
+      channel = 
+       silc_idlist_add_channel(server->global_list, strdup(channel_name), 
+                               0, channel_id, sock->user_data, NULL, NULL, 0);
+      if (!channel)
+       return;
+
       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
@@ -2548,14 +2653,14 @@ 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);
       }
 
       /* 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;
@@ -2706,7 +2811,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;
 
@@ -2823,7 +2929,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;
 
@@ -2832,3 +2939,522 @@ 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");
+    goto out;
+  }
+
+  if (reply && silc_command_get(reply->payload) == SILC_COMMAND_WHOIS) {
+    /* Get entry to the client, and resolve it if we don't have it. */
+    client = silc_idlist_find_client_by_id(server->local_list, 
+                                          r->data, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->global_list,
+                                            r->data, TRUE, NULL);
+      if (!client) {
+       SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+                       "closing connection", sock->hostname, sock->ip));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+       goto out;
+      }
+    }
+
+    if (!(client->mode & SILC_UMODE_DETACHED)) {
+      SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+                     "closing connection", sock->hostname, sock->ip));
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      goto out;
+    }
+  }
+
+  /* Reprocess the packet */
+  silc_server_resume_client(server, sock, r->packet);
+
+ out:
+  silc_socket_free(r->sock);
+  silc_packet_context_free(r->packet);
+  silc_free(r->data);
+  silc_free(r);
+}
+
+/* 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;
+  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;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  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");
+      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_get_client_resolve(server, client_id, FALSE,
+                                                    &resolved);
+    if (!detached_client) {
+      if (resolved) {
+       /* The client info is being resolved. Reprocess this packet after
+          receiving the reply to the query. */
+       SILC_LOG_DEBUG(("Resolving client"));
+       r = silc_calloc(1, sizeof(*r));
+       if (!r)
+         return;
+       r->server = server;
+       r->sock = silc_socket_dup(sock);
+       r->packet = silc_packet_context_dup(packet);
+       r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+       silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+                                   server->cmd_ident,
+                                   silc_server_command_resume_resolve, r);
+      } else {
+       SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+                       "closing connection", sock->hostname, sock->ip));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+      }
+      return;
+    }
+
+    if (!(detached_client->mode & SILC_UMODE_DETACHED))
+      resolve = TRUE;
+    if (!silc_hash_table_count(detached_client->channels) &&
+       detached_client->router)
+      resolve = TRUE;
+    if (!detached_client->nickname)
+      resolve = TRUE;
+
+    if (resolve) {
+      if (server->server_type == SILC_SERVER && !server->standalone) {
+       /* The client info is being resolved. Reprocess this packet after
+          receiving the reply to the query. */
+       SILC_LOG_DEBUG(("Resolving client info"));
+       silc_server_get_client_resolve(server, client_id, TRUE, NULL);
+       r = silc_calloc(1, sizeof(*r));
+       if (!r)
+         return;
+       r->server = server;
+       r->sock = silc_socket_dup(sock);
+       r->packet = silc_packet_context_dup(packet);
+       r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+       silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+                                   server->cmd_ident,
+                                   silc_server_command_resume_resolve, r);
+       return;
+      }
+      if (server->server_type == SILC_SERVER) {
+       SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+                       "closing connection", sock->hostname, sock->ip));
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+       return;
+      }
+    }
+
+    /* Check that we have the public key of the client, if not then we must
+       resolve it first. */
+    if (!detached_client->data.public_key) {
+      if (server->server_type == SILC_SERVER && server->standalone) {
+       silc_server_disconnect_remote(server, sock,
+                                     SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                     "Resuming not possible");
+      } else {
+       /* We must retrieve the detached client's public key by sending
+          GETKEY command. Reprocess this packet after receiving the key */
+       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 : 
+                                server->router->connection,
+                                SILC_COMMAND_GETKEY, ++server->cmd_ident,
+                                1, 1, idp->data, idp->len);
+
+       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);
+       silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+                                   server->cmd_ident,
+                                   silc_server_command_resume_resolve, r);
+
+       silc_buffer_free(idp);
+      }
+      return;
+    } else if (!silc_pkcs_public_key_compare(detached_client->data.public_key,
+                                            idata->public_key)) {
+      /* We require that the connection and resuming authentication data
+        must be using same key pair. */
+      silc_server_disconnect_remote(server, sock,
+                                   SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+                                   "Resuming not possible");
+      return;
+    }
+
+    /* Verify the authentication payload.  This has to be successful in
+       order to allow the resuming */
+    if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+                              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");
+      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->mode &= ~SILC_UMODE_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 */
+    if (!server->standalone)
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_RESUME_CLIENT, 0, 
+                             buf->data, buf->len, 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_idlist_del_client(server->local_list, client);
+    client = detached_client;
+
+    /* 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)) {
+      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);
+         return;
+       }
+       snprintf(&client->nickname[strlen(client->nickname) - 1], 1, 
+                "%d", nickfail);
+      }
+      nick_change = TRUE;
+    }
+
+    if (nick_change) {
+      /* Notify about Client ID change, nickname doesn't actually change. */
+      if (!server->standalone)
+       silc_server_send_notify_nick_change(server, server->router->connection,
+                                           FALSE, client->id, client_id,
+                                           client->nickname);
+    }
+
+    /* Resolve users on those channels that client has joined but we
+       haven't resolved user list yet. */
+    if (server->server_type == SILC_SERVER && !server->standalone) {
+      silc_hash_table_list(client->channels, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+       channel = chl->channel;
+       SILC_LOG_DEBUG(("Resolving users for %s channel", 
+                       channel->channel_name));
+       if (channel->disabled || !channel->users_resolved) {
+         silc_server_send_command(server, server->router->connection,
+                                  SILC_COMMAND_USERS, ++server->cmd_ident,
+                                  1, 2, channel->channel_name,
+                                  strlen(channel->channel_name));
+       }
+      }
+      silc_hash_table_list_reset(&htl);
+    }
+
+    /* Send the new client ID to the client. After this client may start
+       receiving other packets, and may start sending packets too. */
+    silc_server_send_new_id(server, sock, FALSE, client_id, SILC_ID_CLIENT,
+                           silc_id_get_len(client_id, SILC_ID_CLIENT));
+
+    if (nick_change) {
+      /* Send NICK change notify to channels as well. */
+      SilcBuffer oidp, nidp;
+      oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+      nidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+      silc_server_send_notify_on_channels(server, NULL, client, 
+                                         SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+                                         oidp->data, oidp->len, 
+                                         nidp->data, nidp->len,
+                                         client->nickname, 
+                                         strlen(client->nickname));
+      silc_buffer_free(oidp);
+      silc_buffer_free(nidp);
+    }
+
+    /* Add the client again to the ID cache to get it to correct list */
+    if (!silc_idcache_del_by_context(server->local_list->clients, client))
+      silc_idcache_del_by_context(server->global_list->clients, client);
+    silc_free(client->id);
+    client->id = client_id;
+    client_id = NULL;
+    silc_idcache_add(server->local_list->clients, client->nickname,
+                    client->id, client, 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);
+      keyp = 
+       silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+                                                       SILC_ID_CHANNEL), 
+                                       id_string,
+                                       strlen(channel->channel_key->
+                                              cipher->name),
+                                       channel->channel_key->cipher->name,
+                                       channel->key_len / 8, channel->key);
+      silc_free(id_string);
+
+      /* Send the key packet to client */
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                             keyp->data, keyp->len, FALSE);
+
+      if (created && server->server_type == SILC_SERVER && 
+         !server->standalone)
+       silc_server_packet_send(server, server->router->connection, 
+                               SILC_PACKET_CHANNEL_KEY, 0, 
+                               keyp->data, keyp->len, FALSE);
+
+      silc_buffer_free(keyp);
+    }
+    silc_hash_table_list_reset(&htl);
+
+  } 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)
+      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, NULL);
+    if (!detached_client) {
+      detached_client = silc_idlist_find_client_by_id(server->global_list,
+                                                     client_id, TRUE, NULL);
+      if (!detached_client)
+       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_server_kill_client(server, detached_client, NULL,
+                             server->id, SILC_ID_SERVER);
+      return;
+    }
+
+    /* Check whether client is detached at all */
+    if (!(detached_client->mode & SILC_UMODE_DETACHED))
+      return;
+
+    /* Client is detached, and now it is resumed.  Remove the detached
+       mode and mark that it is resumed. */
+    detached_client->mode &= ~SILC_UMODE_DETACHED;
+    detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+
+    /* 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)
+      return;
+
+    /* Get server entry */
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, TRUE, NULL);
+    local = TRUE;
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                  server_id, TRUE, NULL);
+      local = FALSE;
+      if (!server_entry) {
+       silc_free(server_id);
+       return;
+      }
+    }
+
+    if (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER && 
+       server_entry->server_type == SILC_ROUTER)
+      local = FALSE;
+
+    SILC_LOG_DEBUG(("Resuming detached client"));
+
+    /* Change the client to correct list. */
+    if (!silc_idcache_del_by_context(server->local_list->clients,
+                                    detached_client))
+      silc_idcache_del_by_context(server->global_list->clients,
+                                 detached_client);
+    silc_idcache_add(local && server->server_type == SILC_ROUTER ? 
+                    server->local_list->clients : 
+                    server->global_list->clients, 
+                    detached_client->nickname,
+                    detached_client->id, detached_client, FALSE, NULL);
+
+    /* Change the owner of the client if needed */
+    if (detached_client->router != server_entry)
+      detached_client->router = server_entry;
+
+    /* Update channel information regarding global clients on channel. */
+    if (server->server_type == SILC_SERVER) {
+      silc_hash_table_list(detached_client->channels, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void **)&chl))
+       chl->channel->global_users = 
+         silc_server_channel_has_global(chl->channel);
+      silc_hash_table_list_reset(&htl);
+    }
+
+    silc_schedule_task_del_by_context(server->schedule, detached_client);
+
+    /* If the sender of this packet is server and we are router we need to
+       broadcast this packet to other routers in the network. */
+    if (!server->standalone && server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_SERVER &&
+       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+      SILC_LOG_DEBUG(("Broadcasting received Resume Client packet"));
+      silc_server_packet_send(server, server->router->connection,
+                             packet->type, 
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             buffer->data, buffer->len, FALSE);
+      silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                             packet->type, packet->flags,
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE, TRUE);
+    }
+
+    silc_free(server_id);
+  }
+
+  silc_free(client_id);
+}