+
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ SilcPacketContext *packet;
+ void *data;
+} *SilcServerResumeResolve;
+
+SILC_SERVER_CMD_FUNC(resume_resolve)
+{
+ SilcServerResumeResolve r = (SilcServerResumeResolve)context;
+ SilcServer server = r->server;
+ SilcSocketConnection sock = r->sock;
+ SilcServerCommandReplyContext reply = context2;
+ SilcClientEntry client;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!reply || !silc_command_get_status(reply->payload, NULL, NULL)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ if (reply && silc_command_get(reply->payload) == SILC_COMMAND_WHOIS) {
+ /* Get entry to the client, and resolve it if we don't have it. */
+ client = silc_idlist_find_client_by_id(server->local_list,
+ r->data, TRUE, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->global_list,
+ r->data, TRUE, NULL);
+ if (!client) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+ }
+
+ if (!(client->mode & SILC_UMODE_DETACHED)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ client->data.status |= SILC_IDLIST_STATUS_RESUME_RES;
+ }
+
+ /* Reprocess the packet */
+ silc_server_resume_client(server, sock, r->packet);
+
+ out:
+ silc_socket_free(r->sock);
+ silc_packet_context_free(r->packet);
+ silc_free(r->data);
+ silc_free(r);
+}
+
+/* Received client resuming packet. This is used to resume detached
+ client session. It can be sent by the client who wishes to resume
+ but this is also sent by servers and routers to notify other routers
+ that the client is not detached anymore. */
+
+void silc_server_resume_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer, buf;
+ SilcIDListData idata;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcClientEntry detached_client;
+ SilcClientID *client_id = NULL;
+ unsigned char *id_string, *auth = NULL, *nicknamec = NULL;
+ SilcUInt16 id_len, auth_len = 0;
+ int ret;
+ bool resolved, local, nick_change = FALSE, resolve = FALSE;
+ SilcChannelEntry channel;
+ SilcHashTableList htl;
+ SilcChannelClientEntry chl;
+ SilcServerResumeResolve r;
+ const char *cipher;
+
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING(&id_string, &id_len),
+ SILC_STR_END);
+ if (ret != -1)
+ client_id = silc_id_str2id(id_string, id_len, SILC_ID_CLIENT);
+
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ /* Client send this and is attempting to resume to old client session */
+ SilcClientEntry client;
+ SilcBuffer keyp;
+
+ if (ret != -1) {
+ silc_buffer_pull(buffer, 2 + id_len);
+ auth = buffer->data;
+ auth_len = buffer->len;
+ silc_buffer_push(buffer, 2 + id_len);
+ }
+
+ if (!client_id || auth_len < 128) {
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+ return;
+ }
+
+ /* Take client entry of this connection */
+ client = (SilcClientEntry)sock->user_data;
+ idata = (SilcIDListData)client;
+
+ /* Get entry to the client, and resolve it if we don't have it. */
+ detached_client = silc_server_query_client(server, client_id, FALSE,
+ &resolved);
+ if (!detached_client) {
+ if (resolved) {
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ SILC_LOG_DEBUG(("Resolving client"));
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ r->data = client_id;
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+ } else {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+ }
+ return;
+ }
+
+ if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to attach more than once, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+
+ return;
+ }
+
+ if (detached_client->resuming_client &&
+ detached_client->resuming_client != client) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to attach more than once, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+
+ return;
+ }
+
+ if (!detached_client->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;
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ r->data = client_id;
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+ return;
+ }
+ if (server->server_type == SILC_SERVER) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+ return;
+ }
+ }
+
+ /* Check that we have the public key of the client, if not then we must
+ resolve it first. */
+ if (!detached_client->data.public_key) {
+ if (server->server_type == SILC_SERVER && server->standalone) {
+ SILC_LOG_ERROR(("Detached client's public key not present, "
+ "closing connection"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+ } else {
+ /* We must retrieve the detached client's public key by sending
+ GETKEY command. Reprocess this packet after receiving the key */
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ SilcSocketConnection dest_sock =
+ silc_server_get_client_route(server, NULL, 0, client_id, NULL, NULL);
+
+ SILC_LOG_DEBUG(("Resolving client public key"));
+
+ silc_server_send_command(server, dest_sock ? dest_sock :
+ SILC_PRIMARY_ROUTE(server),
+ SILC_COMMAND_GETKEY, ++server->cmd_ident,
+ 1, 1, idp->data, idp->len);
+
+ r = silc_calloc(1, sizeof(*r));
+ if (!r) {
+ silc_free(client_id);
+ return;
+ }
+
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+
+ silc_buffer_free(idp);
+ }
+ silc_free(client_id);
+ return;
+ } else if (!silc_pkcs_public_key_compare(detached_client->data.public_key,
+ idata->public_key)) {
+ /* We require that the connection and resuming authentication data
+ must be using same key pair. */
+ SILC_LOG_ERROR(("Resuming attempted with wrong public key, "
+ "closing connection"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+ return;
+ }
+
+ /* Verify the authentication payload. This has to be successful in
+ order to allow the resuming */
+ if (!idata->hash ||
+ !silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+ detached_client->data.public_key, 0,
+ idata->hash, detached_client->id,
+ SILC_ID_CLIENT)) {
+ SILC_LOG_ERROR(("Client %s (%s) resume authentication failed, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_free(client_id);
+ return;
+ }
+
+ /* 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");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+
+ /* 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)) {
+ silc_free(client_id);
+ if (!silc_id_create_client_id(server, server->id, server->rng,
+ server->md5hash, nicknamec,
+ strlen(nicknamec), &client_id)) {
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BAD_NICKNAME,
+ "Resuming not possible");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+ nick_change = TRUE;
+ }
+
+ /* 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;
+
+ 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);
+
+ /* Take new keys and stuff into use in the old entry */
+ silc_idlist_del_data(detached_client);
+ silc_idlist_add_data(detached_client, idata);
+
+ if (detached_client->data.public_key)
+ silc_hash_table_add(server->pk_hash,
+ detached_client->data.public_key, detached_client);
+
+ 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--;
+
+ /* 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);
+ 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, buf->len, TRUE);
+ silc_server_backup_send(server, client->router,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, buf->len, TRUE, TRUE);
+
+ /* As router we must deliver this packet directly to the original
+ server whom this client was earlier. */
+ if (server->server_type == SILC_ROUTER && client->router &&
+ client->router->server_type != SILC_ROUTER)
+ silc_server_packet_send(server, client->router->connection,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, buf->len, TRUE);
+ 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, 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, nicknamec,
+ client->id, client, 0, NULL);
+
+ /* Send some nice info to the client */
+ silc_server_send_connect_notifys(server, sock, client);
+
+ /* Send all channel keys of channels the client has joined */
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+ bool created = FALSE;
+ channel = chl->channel;
+
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
+ continue;
+
+ /* If we don't have channel key, then create one */
+ if (!channel->channel_key) {
+ if (!silc_server_create_channel_key(server, channel, 0))
+ continue;
+ created = TRUE;
+ }
+
+ id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ cipher = silc_cipher_get_name(channel->channel_key);
+ keyp =
+ silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ id_string,
+ strlen(cipher), cipher,
+ channel->key_len / 8, channel->key);
+ silc_free(id_string);
+
+ /* Send the channel key to the client */
+ silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, keyp->len, FALSE);
+
+ /* Distribute the channel key to channel */
+ if (created) {
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, keyp->len, FALSE, TRUE);
+ }
+
+ silc_buffer_free(keyp);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ } else if (sock->type != SILC_SOCKET_TYPE_CLIENT) {
+ /* Server or router sent this to us to notify that that a client has
+ been resumed. */
+ SilcServerEntry server_entry;
+ SilcServerID *server_id;
+
+ if (!client_id) {
+ SILC_LOG_DEBUG(("Malformed resuming packet"));
+ return;
+ }
+
+ /* Get entry to the client, and resolve it if we don't have it. */
+ detached_client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE,
+ &id_cache);
+ if (!detached_client) {
+ detached_client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE,
+ &id_cache);
+ if (!detached_client) {
+ SILC_LOG_DEBUG(("Resuming client is unknown"));
+ silc_free(client_id);
+ return;
+ }
+ }
+
+ /* Check that the client has not been resumed already because it is
+ protocol error to attempt to resume more than once. The client
+ will be killed if this protocol error occurs. */
+ if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED &&
+ !(detached_client->mode & SILC_UMODE_DETACHED)) {
+ /* The client is clearly attempting to resume more than once and
+ perhaps playing around by resuming from several different places
+ at the same time. */
+ SILC_LOG_DEBUG(("Attempting to re-resume client, killing both"));
+ silc_server_kill_client(server, detached_client, NULL,
+ server->id, SILC_ID_SERVER);
+ silc_free(client_id);
+ return;
+ }
+
+ /* Check whether client is detached at all */
+ if (!(detached_client->mode & SILC_UMODE_DETACHED)) {
+ SILC_LOG_DEBUG(("Client is not detached"));
+ silc_free(client_id);
+ return;
+ }
+
+ /* Check nickname */
+ if (detached_client->nickname) {
+ nicknamec = silc_identifier_check(detached_client->nickname,
+ strlen(detached_client->nickname),
+ SILC_STRING_UTF8, 128, NULL);
+ if (!nicknamec) {
+ silc_free(client_id);
+ return;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Resuming detached client"));
+
+ /* If the sender of this packet is server and we are router we need to
+ broadcast this packet to other routers in the network. */
+ if (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_SERVER &&
+ !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+ SILC_LOG_DEBUG(("Broadcasting received Resume Client packet"));
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ buffer->data, buffer->len, FALSE);
+ silc_server_backup_send(server, sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
+ }
+
+ /* Client is detached, and now it is resumed. Remove the detached
+ mode and mark that it is resumed. */
+
+ if (detached_client->data.public_key)
+ silc_hash_table_del_by_context(server->pk_hash,
+ detached_client->data.public_key,
+ detached_client);
+
+ silc_idlist_del_data(detached_client);
+ detached_client->mode &= ~SILC_UMODE_DETACHED;
+ detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+ detached_client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
+ id_cache->expire = 0;
+
+ /* 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 */
+ server_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!server_id) {
+ silc_free(client_id);
+ return;
+ }
+
+ /* Get server entry */
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, TRUE, NULL);
+ local = FALSE;
+ if (!server_entry) {
+ server_entry = silc_idlist_find_server_by_id(server->local_list,
+ server_id, TRUE, NULL);
+ local = TRUE;
+ if (!server_entry) {
+ silc_free(server_id);
+ silc_free(client_id);
+ return;
+ }
+ }
+
+ if (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ server_entry->server_type == SILC_ROUTER)
+ local = FALSE;
+
+ /* Change the client to correct list. */
+ if (!silc_idcache_del_by_context(server->local_list->clients,
+ detached_client))
+ silc_idcache_del_by_context(server->global_list->clients,
+ detached_client);
+ silc_idcache_add(local && server->server_type == SILC_ROUTER ?
+ server->local_list->clients :
+ server->global_list->clients, nicknamec,
+ detached_client->id, detached_client, FALSE, NULL);
+
+ /* Change the owner of the client */
+ detached_client->router = server_entry;
+
+ /* Update channel information regarding global clients on channel. */
+ if (server->server_type != SILC_ROUTER) {
+ silc_hash_table_list(detached_client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+ chl->channel->global_users =
+ silc_server_channel_has_global(chl->channel);
+ silc_hash_table_list_reset(&htl);
+ }
+
+ silc_free(server_id);
+ }
+
+ silc_free(client_id);
+}