+ silc_server_packet_route(server, dst_sock, packet);
+ silc_packet_free(packet);
+}
+
+typedef struct {
+ SilcServer server;
+ SilcPacketStream sock;
+ SilcPacket packet;
+ SilcClientID client_id;
+} *SilcServerResumeResolve;
+
+SILC_SERVER_CMD_FUNC(resume_resolve)
+{
+ SilcServerResumeResolve r = (SilcServerResumeResolve)context;
+ SilcServer server = r->server;
+ SilcPacketStream sock = r->sock;
+ SilcServerCommandReplyContext reply = context2;
+ SilcClientEntry client;
+ const char *hostname, *ip;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, &ip, NULL);
+
+ if (!reply || !silc_command_get_status(reply->payload, NULL, NULL)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ 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->client_id, TRUE, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->global_list,
+ &r->client_id, TRUE, NULL);
+ if (!client) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ 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", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ 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_packet_stream_unref(r->sock);
+ 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,
+ SilcPacketStream sock,
+ SilcPacket packet)
+{
+ SilcBuffer buffer = &packet->buffer, buf;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ SilcIDCacheEntry id_cache = NULL;
+ SilcClientEntry detached_client;
+ SilcClientID client_id;
+ unsigned char *id_string, *auth = NULL, *nicknamec = NULL;
+ unsigned char cid[32];
+ SilcUInt32 cid_len;
+ SilcUInt16 id_len, auth_len = 0;
+ SilcBool resolved, local, nick_change = FALSE, resolve = FALSE;
+ SilcChannelEntry channel;
+ 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);
+
+ if (silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING(&id_string, &id_len),
+ SILC_STR_END) < 0) {
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ }
+ goto out;
+ }
+
+ silc_id_str2id(id_string, id_len, SILC_ID_CLIENT, &client_id,
+ sizeof(client_id));
+
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ /* Client send this and is attempting to resume to old client session */
+ SilcClientEntry client;
+ SilcBuffer keyp;
+
+ silc_buffer_pull(buffer, 2 + id_len);
+ auth = buffer->data;
+ auth_len = silc_buffer_len(buffer);
+ silc_buffer_push(buffer, 2 + id_len);
+
+ if (auth_len < 128) {
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ /* Take client entry of this connection */
+ client = (SilcClientEntry)idata;
+
+ /* 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)
+ goto out;
+ silc_packet_stream_ref(sock);
+ r->server = server;
+ r->sock = sock;
+ r->packet = packet;
+ r->client_id = client_id;
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+ return;
+ } else {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+ }
+
+ if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to attach more than once, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ if (detached_client->resuming_client &&
+ detached_client->resuming_client != client) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to attach more than once, "
+ "closing connection", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ if (!detached_client->resuming_client)
+ detached_client->resuming_client = client;
+
+ 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;
+ silc_packet_stream_ref(sock);
+ r->server = server;
+ r->sock = sock;
+ r->packet = packet;
+ r->client_id = 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", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+ }
+
+ /* 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");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ } 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);
+ SilcPacketStream 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, silc_buffer_len(idp));
+
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+ silc_packet_stream_ref(sock);
+ r->server = server;
+ r->sock = sock;
+ r->packet = packet;
+ r->client_id = client_id;
+ 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_LOG_ERROR(("Resuming attempted with wrong public key, "
+ "closing connection"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ /* 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", hostname, ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ /* Check nickname */
+ nicknamec = silc_identifier_check(detached_client->nickname,
+ strlen(detached_client->nickname),
+ SILC_STRING_UTF8, 128, NULL);
+ if (!nicknamec) {
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BAD_NICKNAME,
+ "Malformed nickname, cannot resume");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ /* If the ID is not based in our ID then change it */
+ if (!SILC_ID_COMPARE(detached_client->id, server->id,
+ server->id->ip.data_len)) {
+ SilcClientID *new_id;
+ if (!silc_id_create_client_id(server, server->id, server->rng,
+ server->md5hash, nicknamec,
+ strlen(nicknamec), &new_id)) {
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BAD_NICKNAME,
+ "Resuming not possible");
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+ nick_change = TRUE;
+ client_id = *new_id;
+ silc_free(new_id);
+ }
+
+ /* Now resume the client to the network */
+
+ silc_schedule_task_del_by_context(server->schedule, detached_client);
+ silc_packet_set_context(sock, detached_client);
+ detached_client->connection = sock;
+
+ 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) {
+ /* 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;
+ 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--;
+ silc_dlist_del(server->expired_clients, detached_client);
+
+ /* We are finished - reset resuming client */
+ detached_client->resuming_client = NULL;
+
+ /* Check if anyone is watching this client */
+ if (server->server_type == SILC_ROUTER)
+ 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--;
+ 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;
+ silc_free(client->servername);
+ client->servername = strdup(server->server_name);
+
+ /* 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, silc_buffer_len(buf));
+ silc_server_backup_send(server, client->router,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, silc_buffer_len(buf), 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 && client->router &&
+ client->router->server_type != SILC_ROUTER)
+ silc_server_packet_send(server, client->router->connection,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, silc_buffer_len(buf));
+ silc_buffer_free(buf);
+ client->router = NULL;
+
+ 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, silc_buffer_len(oidp),
+ nidp->data, silc_buffer_len(nidp),
+ client->nickname,
+ strlen(client->nickname));
+ silc_buffer_free(oidp);
+ silc_buffer_free(nidp);
+ }
+
+ /* 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);
+
+ /* 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)) {
+ SilcBool 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->send_key) {
+ if (!silc_server_create_channel_key(server, channel, 0))
+ continue;
+ created = TRUE;
+ }
+
+ silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid),
+ &cid_len);
+ cipher = silc_cipher_get_name(channel->send_key);
+ keyp =
+ silc_channel_key_payload_encode(cid_len, cid,
+ strlen(cipher), cipher,
+ channel->key_len / 8, channel->key);
+
+ /* Send the channel key to the client */
+ silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, silc_buffer_len(keyp));
+
+ /* 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, silc_buffer_len(keyp),
+ FALSE, TRUE);
+ }
+
+ silc_buffer_free(keyp);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ } else if (idata->conn_type != SILC_CONN_CLIENT) {
+ /* Server or router sent this to us to notify that that a client has
+ been resumed. */
+ SilcServerEntry server_entry;
+ SilcServerID server_id;
+
+ /* 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"));
+ goto out;
+ }
+ }
+
+ /* 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);
+ goto out;
+ }
+
+ /* Check whether client is detached at all */
+ if (!(detached_client->mode & SILC_UMODE_DETACHED)) {
+ SILC_LOG_DEBUG(("Client is not detached"));
+ goto out;
+ }
+
+ /* Check nickname */
+ if (detached_client->nickname) {
+ nicknamec = silc_identifier_check(detached_client->nickname,
+ strlen(detached_client->nickname),
+ SILC_STRING_UTF8, 128, NULL);
+ if (!nicknamec)
+ goto out;
+ }
+
+ 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 &&
+ idata->conn_type == SILC_CONN_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, silc_buffer_len(buffer));
+ silc_server_backup_send(server, (SilcServerEntry)idata,
+ packet->type, packet->flags,
+ packet->buffer.data,
+ silc_buffer_len(&packet->buffer),
+ FALSE, TRUE);
+ }
+
+ /* Client is detached, and now it is resumed. Remove the detached
+ mode and mark that it is resumed. */
+
+ 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)
+ silc_server_check_watcher_list(server, detached_client, NULL,
+ SILC_NOTIFY_TYPE_UMODE_CHANGE);
+
+ silc_schedule_task_del_by_context(server->schedule, detached_client);
+
+ /* Get the new owner of the resumed client */
+ if (!silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type, &server_id, sizeof(server_id)))
+ goto out;
+
+ /* 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)
+ goto out;
+ }
+
+ if (server->server_type == SILC_ROUTER &&
+ idata->conn_type == SILC_CONN_ROUTER &&
+ server_entry->server_type == SILC_ROUTER)
+ local = FALSE;
+
+ /* 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);
+ }
+
+ /* 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);
+ }
+ }
+
+ out:
+ silc_packet_free(packet);