silcd: increment stat.clients statistics whenever adding new client
[silc.git] / apps / silcd / packet_receive.c
index c6ec4501980cc43f3b725f9b72b6bc7777343d44..2d9ce41f874227a35d55f8248abfb8a5c5978066 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2007 Pekka Riikonen
+  Copyright (C) 1997 - 2009 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
@@ -46,8 +46,17 @@ static void silc_server_notify_process(SilcServer server,
   SilcUInt32 tmp_len, tmp2_len;
   SilcBool local, ret;
 
-  if (idata->conn_type == SILC_CONN_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER || !packet->dst_id) {
+  if (idata->conn_type == SILC_CONN_CLIENT) {
+    SILC_LOG_DEBUG(("Notify received from client, drop it"));
+    return;
+  }
+
+  if (packet->src_id_type != SILC_ID_SERVER){
+    SILC_LOG_DEBUG(("Bad notify packet received"));
+    return;
+  }
+
+  if (!packet->dst_id) {
     SILC_LOG_DEBUG(("Bad notify packet received"));
     return;
   }
@@ -69,8 +78,10 @@ static void silc_server_notify_process(SilcServer server,
 
   /* Parse the Notify Payload */
   payload = silc_notify_payload_parse(buffer->data, silc_buffer_len(buffer));
-  if (!payload)
+  if (!payload) {
+    SILC_LOG_DEBUG(("Marlformed notify payload"));
     return;
+  }
 
   /* If we are router and this packet is not already broadcast packet
      we will broadcast it. The sending socket really cannot be router or
@@ -84,8 +95,10 @@ static void silc_server_notify_process(SilcServer server,
       /* Packet is destined to channel */
       if (!silc_id_str2id(packet->dst_id, packet->dst_id_len,
                          packet->dst_id_type, &channel_id,
-                         sizeof(channel_id)))
+                         sizeof(channel_id))) {
+       SILC_LOG_DEBUG(("Malformed destination ID in notify packet"));
        goto out;
+      }
 
       silc_server_packet_send_dest(server, SILC_PRIMARY_ROUTE(server),
                                   packet->type, packet->flags |
@@ -112,8 +125,10 @@ static void silc_server_notify_process(SilcServer server,
 
   type = silc_notify_get_type(payload);
   args = silc_notify_get_args(payload);
-  if (!args)
+  if (!args) {
+    SILC_LOG_DEBUG(("Notify doesn't have any arguments, drop it"));
     goto out;
+  }
 
   switch(type) {
   case SILC_NOTIFY_TYPE_JOIN:
@@ -133,7 +148,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               SILC_ID_GET_ID(id), NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -170,6 +186,12 @@ static void silc_server_notify_process(SilcServer server,
        }
 
        client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+       if (idata->conn_type == SILC_CONN_SERVER)
+         server->stat.cell_clients++;
+       SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                       server->stat.clients + 1));
+       server->stat.clients++;
       }
     }
 
@@ -251,7 +273,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               &channel_id, NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(&channel_id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -309,6 +332,9 @@ static void silc_server_notify_process(SilcServer server,
       tmp_len = 128;
 
     /* Update statistics */
+    SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                   server->stat.clients - 1));
+    SILC_VERIFY(server->stat.clients > 0);
     server->stat.clients--;
     if (server->stat.cell_clients)
       server->stat.cell_clients--;
@@ -316,10 +342,12 @@ static void silc_server_notify_process(SilcServer server,
     SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
     silc_schedule_task_del_by_context(server->schedule, client);
 
-    /* Remove from public key hash table. */
-    if (client->data.public_key)
-      silc_hash_table_del_by_context(server->pk_hash, client->data.public_key,
-                                     client);
+    /* Remove client's public key from repository, this will free it too. */
+    if (client->data.public_key) {
+      silc_skr_del_public_key(server->repository, client->data.public_key,
+                             client);
+      client->data.public_key = NULL;
+    }
 
     /* Remove the client from all channels. */
     silc_server_remove_from_channels(server, NULL, client, TRUE,
@@ -337,6 +365,9 @@ static void silc_server_notify_process(SilcServer server,
     client->mode = 0;
     client->router = NULL;
     client->connection = NULL;
+    client->data.created = silc_time();
+    silc_dlist_del(server->expired_clients, client);
+    silc_dlist_add(server->expired_clients, client);
     break;
 
   case SILC_NOTIFY_TYPE_TOPIC_SET:
@@ -380,7 +411,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               &channel_id, NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(&channel_id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -500,7 +532,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               &channel_id, NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(&channel_id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -820,7 +853,8 @@ static void silc_server_notify_process(SilcServer server,
        channel = silc_idlist_find_channel_by_id(server->local_list,
                                                 &channel_id, NULL);
        if (!channel) {
-         SILC_LOG_DEBUG(("Notify for unknown channel"));
+         SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                         silc_id_render(&channel_id, SILC_ID_CHANNEL)));
          goto out;
        }
       }
@@ -1007,7 +1041,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               SILC_ID_GET_ID(id), NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -1076,7 +1111,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->global_list,
                                               SILC_ID_GET_ID(id), NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -1205,12 +1241,12 @@ static void silc_server_notify_process(SilcServer server,
 
            /* Get client entry */
            client = silc_idlist_find_client_by_id(server->global_list,
-                                                  SILC_ID_GET_ID(id),
+                                                  SILC_ID_GET_ID(id2),
                                                   TRUE, &cache);
            local = FALSE;
            if (!client) {
              client = silc_idlist_find_client_by_id(server->local_list,
-                                                    SILC_ID_GET_ID(id),
+                                                    SILC_ID_GET_ID(id2),
                                                     TRUE, &cache);
              local = TRUE;
              if (!client)
@@ -1218,6 +1254,9 @@ static void silc_server_notify_process(SilcServer server,
            }
 
            /* Update statistics */
+           SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                           server->stat.clients - 1));
+           SILC_VERIFY(server->stat.clients > 0);
            server->stat.clients--;
            if (server->stat.cell_clients)
              server->stat.cell_clients--;
@@ -1237,13 +1276,8 @@ static void silc_server_notify_process(SilcServer server,
            if (local)
              silc_server_del_from_watcher_list(server, client);
 
-           /* Remove from public key hash table. */
-           if (client->data.public_key)
-             silc_hash_table_del_by_context(server->pk_hash,
-                                             client->data.public_key,
-                                             client);
-
            /* Remove the client */
+           silc_dlist_del(server->expired_clients, client);
            silc_idlist_del_data(client);
            silc_idlist_del_client(local ? server->local_list :
                                   server->global_list, client);
@@ -1307,7 +1341,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               &channel_id, NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -1459,13 +1494,17 @@ static void silc_server_notify_process(SilcServer server,
        silc_server_check_watcher_list(server, client, NULL,
                                       SILC_NOTIFY_TYPE_KILLED);
 
-      /* Remove from public key hash table. */
-      if (client->data.public_key)
-       silc_hash_table_del_by_context(server->pk_hash,
-                                      client->data.public_key,
-                                      client);
+      /* Remove client's public key from repository, this will free it too. */
+      if (client->data.public_key) {
+       silc_skr_del_public_key(server->repository, client->data.public_key,
+                               client);
+       client->data.public_key = NULL;
+      }
 
       /* Update statistics */
+      SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                     server->stat.clients - 1));
+      SILC_VERIFY(server->stat.clients > 0);
       server->stat.clients--;
       if (server->stat.cell_clients)
        server->stat.cell_clients--;
@@ -1473,7 +1512,8 @@ static void silc_server_notify_process(SilcServer server,
       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 
       if (SILC_IS_LOCAL(client)) {
-       server->stat.my_clients--;
+       if (!client->local_detached)
+         server->stat.my_clients--;
        silc_schedule_task_del_by_context(server->schedule, client);
        silc_idlist_del_data(client);
        client->mode = 0;
@@ -1483,6 +1523,9 @@ static void silc_server_notify_process(SilcServer server,
       client->mode = 0;
       client->router = NULL;
       client->connection = NULL;
+      client->data.created = silc_time();
+      silc_dlist_del(server->expired_clients, client);
+      silc_dlist_add(server->expired_clients, client);
       break;
     }
 
@@ -1565,7 +1608,8 @@ static void silc_server_notify_process(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list,
                                               SILC_ID_GET_ID(id), NULL);
       if (!channel) {
-       SILC_LOG_DEBUG(("Notify for unknown channel"));
+       SILC_LOG_DEBUG(("Notify for unknown channel %s",
+                       silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -1639,13 +1683,9 @@ static void silc_server_notify_process(SilcServer server,
        if (!client)
          goto out;
 
-       if (client->data.public_key)
-         silc_hash_table_del_by_context(server->pk_hash,
-                                        client->data.public_key,
-                                        client);
-
        silc_server_remove_from_channels(server, NULL, client, TRUE,
                                         NULL, TRUE, FALSE);
+       silc_dlist_del(server->expired_clients, client);
        silc_idlist_del_data(client);
        silc_idlist_del_client(server->global_list, client);
       }
@@ -1658,6 +1698,7 @@ static void silc_server_notify_process(SilcServer server,
     break;
 
   default:
+    SILC_LOG_DEBUG(("Unsupported notify %d", type));
     break;
   }
 
@@ -1891,7 +1932,6 @@ void silc_server_channel_message(SilcServer server,
   SilcChannelID id;
   SilcClientID cid;
   SilcID sid;
-  void *sender_id = NULL;
   SilcClientEntry sender_entry = NULL;
   SilcIDListData idata;
   SilcChannelClientEntry chl;
@@ -1998,9 +2038,9 @@ 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_id,
-                                     packet->src_id_type, sender_entry,
-                                     packet->buffer.data,
+  silc_server_packet_relay_to_channel(server, sock, channel,
+                                     SILC_ID_GET_ID(sid), sid.type,
+                                     sender_entry, packet->buffer.data,
                                      silc_buffer_len(&packet->buffer));
 
  out:
@@ -2123,12 +2163,30 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     username[username_len - 1] = '\0';
   }
 
+  /* Take nickname from NEW_CLIENT packet, if present */
+  if (silc_buffer_unformat(buffer,
+                          SILC_STR_UI16_NSTRING_ALLOC(&nickname,
+                                                      &nickname_len),
+                          SILC_STR_END) >= 0) {
+    if (nickname_len > 128) {
+      nickname_len = 128;
+      nickname[nickname_len - 1] = '\0';
+    }
+  }
+
+  /* Nickname is initially same as username, if not present in NEW_CLIENT */
+  if (!nickname) {
+    nickname = strdup(username);
+    nickname_len = strlen(nickname);
+  }
+
   /* Check for valid username string */
-  nicknamec = silc_identifier_check(username, username_len,
+  nicknamec = silc_identifier_check(nickname, nickname_len,
                                    SILC_STRING_UTF8, 128, &tmp_len);
   if (!nicknamec) {
     silc_free(username);
     silc_free(realname);
+    silc_free(nickname);
     SILC_LOG_ERROR(("Client %s (%s) sent bad username string '%s', closing "
                    "connection", hostname, ip, username));
     silc_server_disconnect_remote(server, sock,
@@ -2139,21 +2197,6 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     return NULL;
   }
 
-  /* Take nickname from NEW_CLIENT packet, if present */
-  if (silc_buffer_unformat(buffer,
-                          SILC_STR_UI16_NSTRING_ALLOC(&nickname,
-                                                      &nickname_len),
-                          SILC_STR_END)) {
-    if (nickname_len > 128) {
-      nickname_len = 128;
-      nickname[nickname_len - 1] = '\0';
-    }
-  }
-
-  /* Nickname is initially same as username, if not present in NEW_CLIENT */
-  if (!nickname)
-    nickname = strdup(username);
-
   /* Make sanity checks for the hostname of the client. If the hostname
      is provided in the `username' check that it is the same than the
      resolved hostname, or if not resolved the hostname that appears in
@@ -2167,6 +2210,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
     host = silc_memdup(username + tlen + 1, strlen(username) - tlen - 1);
 
     if (strcmp(hostname, ip) && strcmp(hostname, host)) {
+      silc_free(nickname);
       silc_free(username);
       silc_free(host);
       silc_free(realname);
@@ -2184,6 +2228,7 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                                        client->data.public_key);
     phostname = strdup(silc_pubkey->identifier.host);
     if (!strcmp(hostname, ip) && phostname && strcmp(phostname, host)) {
+      silc_free(nickname);
       silc_free(username);
       silc_free(host);
       silc_free(phostname);
@@ -2639,7 +2684,8 @@ static void silc_server_new_id_real(SilcServer server,
         global list. Cell wide information however is kept in the local
         list. */
       entry = silc_idlist_add_client(id_list, NULL, NULL, NULL,
-                                    &id, router, NULL);
+                                    silc_id_dup(&id, SILC_ID_CLIENT),
+                                    router, NULL);
       if (!entry) {
        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
 
@@ -2652,6 +2698,8 @@ static void silc_server_new_id_real(SilcServer server,
 
       if (idata->conn_type == SILC_CONN_SERVER)
        server->stat.cell_clients++;
+      SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                     server->stat.clients + 1));
       server->stat.clients++;
 
       /* Check if anyone is watching this nickname */
@@ -2659,19 +2707,20 @@ static void silc_server_new_id_real(SilcServer server,
        silc_server_check_watcher_list(server, entry, NULL, 0);
 
       if (server->server_type == SILC_ROUTER) {
-       /* Add the client's public key to hash table or get the key with
+       /* Add the client's public key to repository or get the key with
           GETKEY command. */
         if (entry->data.public_key) {
-         if (!silc_hash_table_find_by_context(server->pk_hash,
-                                              entry->data.public_key,
-                                              entry, NULL))
-           silc_hash_table_add(server->pk_hash, entry->data.public_key,
-                               entry);
-       } else
+         if (!silc_server_get_public_key_by_client(server, entry, NULL))
+           silc_skr_add_public_key_simple(server->repository,
+                                          entry->data.public_key,
+                                          SILC_SKR_USAGE_IDENTIFICATION,
+                                          entry, NULL);
+       } else {
          silc_server_send_command(server, router_sock,
                                   SILC_COMMAND_GETKEY, ++server->cmd_ident,
                                   1, 1, buffer->data,
                                   silc_buffer_len(buffer));
+       }
       }
     }
     break;
@@ -2717,7 +2766,8 @@ static void silc_server_new_id_real(SilcServer server,
       /* 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,
+                                    silc_id_dup(&id, SILC_ID_SERVER), router,
                                     router_sock);
       if (!entry) {
        SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
@@ -2959,9 +3009,12 @@ static void silc_server_new_channel_process(SilcServer server,
       }
 
       /* Create the channel with the provided Channel ID */
-      channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
-                                                      channel_name,
-                                                      &channel_id, FALSE);
+      channel =
+       silc_server_create_new_channel_with_id(
+                                    server, NULL, NULL,
+                                    channel_name,
+                                    silc_id_dup(&channel_id, SILC_ID_CHANNEL),
+                                    FALSE);
       if (!channel) {
        silc_channel_payload_free(payload);
        return;
@@ -3401,8 +3454,11 @@ void silc_server_resume_client(SilcServer server,
   SilcHashTableList htl;
   SilcChannelClientEntry chl;
   SilcServerResumeResolve r;
+  SilcPublicKey public_key;
   const char *cipher, *hostname, *ip;
 
+  SILC_LOG_DEBUG(("Resuming client"));
+
   silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
                              NULL, &hostname, &ip, NULL);
 
@@ -3647,21 +3703,35 @@ void silc_server_resume_client(SilcServer server,
     silc_packet_set_context(sock, detached_client);
     detached_client->connection = sock;
 
-    if (detached_client->data.public_key)
-      silc_hash_table_del_by_context(server->pk_hash,
-                                    detached_client->data.public_key,
-                                    detached_client);
-    if (idata->public_key)
-      silc_hash_table_del_by_context(server->pk_hash,
-                                    idata->public_key, idata);
+    if (detached_client->data.public_key) {
+      /* Delete the detached client's public key from repository */
+      silc_skr_del_public_key(server->repository,
+                             detached_client->data.public_key,
+                             detached_client);
+      detached_client->data.public_key = NULL;
+    }
+
+    if (idata->public_key) {
+      /* Delete the resuming client's public key from repository.  It will
+        be added later again. */
+      public_key = silc_pkcs_public_key_copy(idata->public_key);
+      silc_skr_del_public_key(server->repository, idata->public_key, idata);
+      idata->public_key = public_key;
+    }
 
     /* Take new keys and stuff into use in the old entry */
     silc_idlist_del_data(detached_client);
     silc_idlist_add_data(detached_client, idata);
+    idata->public_key = NULL;
 
-    if (detached_client->data.public_key)
-      silc_hash_table_add(server->pk_hash,
-                         detached_client->data.public_key, detached_client);
+    if (detached_client->data.public_key) {
+      /* Add the resumed client's public key back to repository. */
+      if (!silc_server_get_public_key_by_client(server, detached_client, NULL))
+       silc_skr_add_public_key_simple(server->repository,
+                                      detached_client->data.public_key,
+                                      SILC_SKR_USAGE_IDENTIFICATION,
+                                      detached_client, NULL);
+    }
 
     detached_client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
@@ -3669,6 +3739,7 @@ void silc_server_resume_client(SilcServer server,
     detached_client->data.status &= ~SILC_IDLIST_STATUS_RESUME_RES;
     detached_client->mode &= ~SILC_UMODE_DETACHED;
     server->stat.my_detached--;
+    silc_dlist_del(server->expired_clients, detached_client);
 
     /* We are finished - reset resuming client */
     detached_client->resuming_client = NULL;
@@ -3678,14 +3749,23 @@ void silc_server_resume_client(SilcServer server,
       silc_server_check_watcher_list(server, detached_client, NULL,
                                     SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
-    /* Delete this current client entry since we're resuming to old one. */
-    server->stat.my_clients--;
+    /* Delete this current client entry since we're resuming to old one.
+       We decrement clients/cell_clients as we are getting rid of the
+       current client and replacing it with the detached one.  We keep the
+       server user count as-is (incremented by the current client entry) as
+       we decremented the count already during detach, thus we'd be undoing
+       that operation. */
+    detached_client->local_detached = FALSE;
+    SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                   server->stat.clients - 1));
+    SILC_VERIFY(server->stat.clients > 0);
     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);
+    silc_dlist_del(server->expired_clients, client);
     if (!silc_idlist_del_client(server->local_list, client))
       silc_idlist_del_client(server->global_list, client);
     client = detached_client;
@@ -3764,14 +3844,17 @@ void silc_server_resume_client(SilcServer server,
       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,
-                                    NULL))
-      silc_idcache_del_by_context(server->global_list->clients, client, NULL);
-    silc_free(client->id);
-    *client->id = client_id;
-    silc_idcache_add(server->local_list->clients, nicknamec,
-                    client->id, client);
+    /* Update entry */
+    if (!silc_idcache_update_by_context(server->local_list->clients, client,
+                                       &client_id, NULL, FALSE))
+      silc_idcache_update_by_context(server->global_list->clients, client,
+                                    &client_id, NULL, FALSE);
+
+    /* Move entry to local list if it is in global list */
+    if (silc_idcache_find_by_context(server->global_list->clients, client,
+                                    &id_cache))
+      silc_idcache_move(server->global_list->clients,
+                       server->local_list->clients, id_cache);
 
     /* Send some nice info to the client */
     silc_server_send_connect_notifys(server, sock, client);
@@ -3886,18 +3969,39 @@ void silc_server_resume_client(SilcServer server,
                              FALSE, TRUE);
     }
 
+    /* If the client has a locally-connected previous owner, then we
+       will need to notify them that the resume has completed.  Note
+       that if the previous owner was a router, this case is already
+       handled above by the broadcast, so we shouldn't attempt to
+       send another notification in that case.   Additionally, if
+       the previous owner was the server that sent the packet, then
+       we'll not send the notification as it will have already done
+       the necessary work locally. */
+    if (server->server_type == SILC_ROUTER &&
+       idata->conn_type == SILC_CONN_SERVER &&
+       detached_client->router &&
+       SILC_IS_LOCAL(detached_client->router) &&
+       detached_client->router->server_type != SILC_ROUTER)
+      silc_server_packet_send(server, detached_client->router->connection,
+                             SILC_PACKET_RESUME_CLIENT, 0,
+                             buffer->data, silc_buffer_len(buffer));
+
     /* Client is detached, and now it is resumed.  Remove the detached
        mode and mark that it is resumed. */
 
-    if (detached_client->data.public_key)
-      silc_hash_table_del_by_context(server->pk_hash,
-                                    detached_client->data.public_key,
-                                    detached_client);
+    if (detached_client->data.public_key) {
+      /* Delete the detached client's public key from repository */
+      silc_skr_del_public_key(server->repository,
+                             detached_client->data.public_key,
+                             detached_client);
+      detached_client->data.public_key = NULL;
+    }
 
     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;
+    silc_dlist_del(server->expired_clients, detached_client);
 
     /* Check if anyone is watching this client */
     if (server->server_type == SILC_ROUTER)
@@ -3928,16 +4032,23 @@ void silc_server_resume_client(SilcServer server,
        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, NULL))
-      silc_idcache_del_by_context(server->global_list->clients,
-                                 detached_client, NULL);
-    silc_idcache_add(local && server->server_type == SILC_ROUTER ?
-                    server->local_list->clients :
-                    server->global_list->clients, nicknamec,
-                    detached_client->id, detached_client);
+    /* Move entry to correct list */
+    if (local && server->server_type == SILC_ROUTER) {
+      if (silc_idcache_find_by_context(server->global_list->clients,
+                                      detached_client, &id_cache))
+       silc_idcache_move(server->global_list->clients,
+                         server->local_list->clients, id_cache);
+    } else {
+      if (silc_idcache_find_by_context(server->local_list->clients,
+                                      detached_client, &id_cache))
+       silc_idcache_move(server->local_list->clients,
+                         server->global_list->clients, id_cache);
+      }
 
+    /* We don't own this client anymore, if we ever did, as we were just
+     * told that someone else resumed it.  Thus, it is most definitely no
+     * a detached client.*/
+    detached_client->local_detached = FALSE;
     /* Change the owner of the client */
     detached_client->router = server_entry;