X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient_register.c;h=efbeaa24d9cae4534d01d65ad53e221b9ec83fb7;hp=42cb36772e682fa5a670193c1126e7bb7cffb1ef;hb=HEAD;hpb=77336860c5d419c9d25a6366de0269c0edb38889 diff --git a/lib/silcclient/client_register.c b/lib/silcclient/client_register.c index 42cb3677..efbeaa24 100644 --- a/lib/silcclient/client_register.c +++ b/lib/silcclient/client_register.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2006 Pekka Riikonen + Copyright (C) 2006 - 2007 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 @@ -28,30 +28,15 @@ typedef struct { SilcClient client; SilcClientConnection conn; SilcBufferStruct detach; + SilcBuffer auth; char *nickname; - SilcClientID client_id; + unsigned char *id; + SilcUInt32 id_len; SilcUInt32 channel_count; - SilcUInt32 *cmd_idents; - SilcUInt32 cmd_idents_count; - SilcBool success; } *SilcClientResumeSession; /************************ Static utility functions **************************/ -/* Command callback. Nothing interesting to do here. */ - -static SilcBool -silc_client_register_command_called(SilcClient client, - SilcClientConnection conn, - SilcCommand command, - SilcStatus status, - SilcStatus error, - void *context, - va_list ap) -{ - return FALSE; -} - /* Continues resuming after resolving. Continue after last reply. */ static SilcBool @@ -72,6 +57,38 @@ silc_client_resume_continue(SilcClient client, return TRUE; } +/* Function used to call command replies back to application in resuming. */ + +static void +silc_client_resume_command_callback(SilcClient client, + SilcClientConnection conn, + SilcCommand command, ...) +{ + va_list ap; + va_start(ap, command); + client->internal->ops->command_reply(client, conn, command, + SILC_STATUS_OK, SILC_STATUS_OK, ap); + va_end(ap); +} + +/* Resume authentication data generation callback */ + +static void silc_client_resume_auth_generated(const SilcBuffer data, + void *context) +{ + SilcClientConnection conn = context; + SilcClientResumeSession resume = + silc_fsm_get_state_context(&conn->internal->event_thread); + + if (!data) + silc_fsm_next(&conn->internal->event_thread, silc_client_st_resume_error); + else + resume->auth = silc_buffer_copy(data); + + SILC_FSM_CALL_CONTINUE_SYNC(&conn->internal->event_thread); +} + + /****************************** NEW_ID packet *******************************/ /* Received new ID packet from server during registering to SILC network */ @@ -81,6 +98,7 @@ SILC_FSM_STATE(silc_client_new_id) SilcClientConnection conn = fsm_context; SilcClient client = conn->client; SilcPacket packet = state_context; + char *nick; SilcID id; if (conn->local_id) @@ -95,29 +113,25 @@ SILC_FSM_STATE(silc_client_new_id) SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id, SILC_ID_CLIENT))); + /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */ + if (conn->internal->remote_version >= 13) + nick = (conn->internal->params.nickname ? + conn->internal->params.nickname : client->username); + else + nick = client->username; + /* Create local client entry */ - conn->local_entry = silc_client_add_client(client, conn, - client->username, + conn->local_entry = silc_client_add_client(client, conn, nick, client->username, client->realname, &id.u.client_id, 0); if (!conn->local_entry) goto out; - /* Save the ID */ + /* Save the ID. Take reference to conn->local_id. */ conn->local_id = &conn->local_entry->id; conn->internal->local_idp = silc_buffer_copy(&packet->buffer); - /* Save cache entry */ - silc_mutex_lock(conn->internal->lock); - if (!silc_idcache_find_by_id_one(conn->internal->client_cache, - conn->local_id, - &conn->internal->local_entry)) { - silc_mutex_unlock(conn->internal->lock); - goto out; - } - silc_mutex_unlock(conn->internal->lock); - /* Save remote ID */ if (packet->src_id_len) { conn->internal->remote_idp = @@ -155,9 +169,15 @@ SILC_FSM_STATE(silc_client_st_register) { SilcClientConnection conn = fsm_context; SilcClient client = conn->client; + char *nick = NULL; SILC_LOG_DEBUG(("Register to network")); + /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */ + if (conn->internal->remote_version >= 13) + nick = (conn->internal->params.nickname ? + conn->internal->params.nickname : client->username); + /* Send NEW_CLIENT packet to register to network */ if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0, SILC_STR_UI_SHORT(strlen(client->username)), @@ -166,6 +186,8 @@ SILC_FSM_STATE(silc_client_st_register) SILC_STR_UI_SHORT(strlen(client->realname)), SILC_STR_DATA(client->realname, strlen(client->realname)), + SILC_STR_UI_SHORT(nick ? strlen(nick) : 0), + SILC_STR_DATA(nick, nick ? strlen(nick) : 0), SILC_STR_END)) { /** Error sending packet */ silc_fsm_next(fsm, silc_client_st_register_error); @@ -186,12 +208,6 @@ SILC_FSM_STATE(silc_client_st_register_complete) SilcClientConnection conn = fsm_context; SilcClient client = conn->client; - if (conn->internal->aborted) { - /** Aborted */ - silc_fsm_next(fsm, silc_client_st_register_error); - return SILC_FSM_CONTINUE; - } - if (conn->internal->disconnected) { /** Disconnected */ silc_fsm_next(fsm, silc_client_st_register_error); @@ -222,13 +238,13 @@ SILC_FSM_STATE(silc_client_st_register_complete) /* Issue IDENTIFY command for itself to get resolved hostname correctly from server. */ silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, - silc_client_register_command_called, NULL, + silc_client_command_called_dummy, NULL, 1, 5, silc_buffer_data(conn->internal->local_idp), silc_buffer_len(conn->internal->local_idp)); - /* Call NICK command if the nickname was set by the application (and is - not same as the username). */ - if (conn->internal->params.nickname && + /* With SILC protocol version 1.2 call NICK command if the nickname was + set by the application. */ + if (conn->internal->params.nickname && conn->internal->remote_version < 13 && !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username)) silc_client_command_call(client, conn, NULL, "NICK", conn->internal->params.nickname, NULL); @@ -236,7 +252,7 @@ SILC_FSM_STATE(silc_client_st_register_complete) /* Issue INFO command to fetch the real server name and server information and other stuff. */ silc_client_command_send(client, conn, SILC_COMMAND_INFO, - silc_client_register_command_called, NULL, + silc_client_command_called_dummy, NULL, 1, 2, silc_buffer_data(conn->internal->remote_idp), silc_buffer_len(conn->internal->remote_idp)); @@ -247,6 +263,8 @@ SILC_FSM_STATE(silc_client_st_register_complete) conn->internal->registering = FALSE; silc_schedule_task_del_by_all(conn->internal->schedule, 0, silc_client_connect_timeout, conn); + silc_async_free(conn->internal->cop); + conn->internal->cop = NULL; return SILC_FSM_FINISH; } @@ -256,22 +274,16 @@ SILC_FSM_STATE(silc_client_st_register_complete) SILC_FSM_STATE(silc_client_st_register_error) { SilcClientConnection conn = fsm_context; - SilcClient client = conn->client; SILC_LOG_DEBUG(("Error registering to network")); /* Signal to close connection */ + conn->internal->status = SILC_CLIENT_CONN_ERROR; if (!conn->internal->disconnected) { conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event); } - /* Call connect callback */ - if (conn->internal->callback_called) - conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL, - conn->callback_context); - conn->internal->callback_called = TRUE; - silc_schedule_task_del_by_all(conn->internal->schedule, 0, silc_client_connect_timeout, conn); @@ -287,9 +299,9 @@ SILC_FSM_STATE(silc_client_st_resume) SilcClientConnection conn = fsm_context; SilcClient client = conn->client; SilcClientResumeSession resume; - SilcBuffer auth; unsigned char *id; SilcUInt16 id_len; + SilcClientID client_id; int ret; SILC_LOG_DEBUG(("Resuming detached session")); @@ -318,38 +330,49 @@ SILC_FSM_STATE(silc_client_st_resume) SILC_STR_END); if (ret < 0) { /** Malformed detach data */ + SILC_LOG_DEBUG(("Malformed detachment data")); silc_fsm_next(fsm, silc_client_st_resume_error); return SILC_FSM_CONTINUE; } - if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id, - sizeof(resume->client_id))) { + if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id, + sizeof(client_id))) { /** Malformed ID */ + SILC_LOG_DEBUG(("Malformed ID")); silc_fsm_next(fsm, silc_client_st_resume_error); return SILC_FSM_CONTINUE; } + resume->id = id; + resume->id_len = id_len; /* Generate authentication data that server will verify */ - auth = silc_auth_public_key_auth_generate(conn->public_key, - conn->private_key, - client->rng, - conn->internal->hash, - &resume->client_id, - SILC_ID_CLIENT); - if (!auth) { - /** Out of memory */ - silc_fsm_next(fsm, silc_client_st_resume_error); - return SILC_FSM_CONTINUE; - } + silc_fsm_next(fsm, silc_client_st_resume_send); + SILC_FSM_CALL(silc_auth_public_key_auth_generate( + conn->public_key, conn->private_key, + client->rng, conn->internal->hash, + &client_id, SILC_ID_CLIENT, + silc_client_resume_auth_generated, conn)); + /* NOT REACHED */ +} + +/* Send RESUME_CLIENT packet */ + +SILC_FSM_STATE(silc_client_st_resume_send) +{ + SilcClientConnection conn = fsm_context; + SilcClientResumeSession resume = state_context; + + SILC_LOG_DEBUG(("Send RESUME_CLIENT packet")); /* Send RESUME_CLIENT packet to resume to network */ if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0, - SILC_STR_UI_SHORT(id_len), - SILC_STR_DATA(id, id_len), - SILC_STR_DATA(silc_buffer_data(auth), - silc_buffer_len(auth)), + SILC_STR_UI_SHORT(resume->id_len), + SILC_STR_DATA(resume->id, resume->id_len), + SILC_STR_DATA(silc_buffer_data(resume->auth), + silc_buffer_len(resume->auth)), SILC_STR_END)) { /** Error sending packet */ + SILC_LOG_DEBUG(("Error sending packet")); silc_fsm_next(fsm, silc_client_st_resume_error); return SILC_FSM_CONTINUE; } @@ -371,6 +394,12 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels) unsigned char **res_argv = NULL; int i; + if (conn->internal->disconnected) { + /** Disconnected */ + silc_fsm_next(fsm, silc_client_st_resume_error); + return SILC_FSM_CONTINUE; + } + if (!conn->local_id) { /** Timeout, ID not received */ conn->internal->registering = FALSE; @@ -378,32 +407,55 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels) return SILC_FSM_CONTINUE; } - /* First, send UMODE command to get our own user mode in the network */ + /** Wait for channels */ + silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes); + + /* Change our nickname */ + silc_client_change_nickname(client, conn, conn->local_entry, + resume->nickname, NULL, NULL, 0); + + /* Send UMODE command to get our own user mode in the network */ SILC_LOG_DEBUG(("Resolving user mode")); silc_client_command_send(client, conn, SILC_COMMAND_UMODE, - silc_client_register_command_called, NULL, + silc_client_command_called_dummy, NULL, 1, 1, silc_buffer_data(conn->internal->local_idp), silc_buffer_len(conn->internal->local_idp)); - /* Second, send IDENTIFY command for all channels we know about. These - are the channels we've joined to according our detachment data. */ + if (!resume->channel_count) + return SILC_FSM_YIELD; + + /* Send IDENTIFY command for all channels we know about. These are the + channels we've joined to according our detachment data. */ for (i = 0; i < resume->channel_count; i++) { - SilcChannelID channel_id; + SilcChannelEntry channel; unsigned char *chid; SilcUInt16 chid_len; SilcBuffer idp; + SilcChannelID channel_id; + char *name; if (silc_buffer_unformat(&resume->detach, SILC_STR_ADVANCE, - SILC_STR_UI16_NSTRING(NULL, NULL), + SILC_STR_UI16_NSTRING(&name, NULL), SILC_STR_UI16_NSTRING(&chid, &chid_len), SILC_STR_UI_INT(NULL), SILC_STR_END) < 0) continue; + if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id, + sizeof(channel_id))) + continue; idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL); if (!idp) continue; + + /* Add the channel to cache */ + channel = silc_client_get_channel_by_id(client, conn, &channel_id); + if (!channel) + silc_client_add_channel(client, conn, name, 0, &channel_id); + else + silc_client_unref_channel(client, conn, channel); + res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1)); res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) * (res_argc + 1)); @@ -428,35 +480,181 @@ SILC_FSM_STATE(silc_client_st_resume_resolve_channels) silc_free(res_argv_lens); silc_free(res_argv_types); - /** Wait for channels */ - silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes); return SILC_FSM_WAIT; } -/* Resolve joined channel modes. */ +/* Resolve joined channel modes, users and topics. */ SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes) { SilcClientConnection conn = fsm_context; + SilcClient client = conn->client; SilcClientResumeSession resume = state_context; - SilcHashTableList htl; - SilcChannelUser chu; + SilcIDCacheEntry entry; + SilcChannelEntry channel; + SilcList channels; + SilcBuffer idp; + + if (conn->internal->disconnected) { + /** Disconnected */ + silc_fsm_next(fsm, silc_client_st_resume_error); + return SILC_FSM_CONTINUE; + } - SILC_LOG_DEBUG(("Resolving joined channel modes")); + SILC_LOG_DEBUG(("Resolving channel details")); - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { + /** Wait for channel modes */ + silc_fsm_next(fsm, silc_client_st_resume_completed); + + if (!silc_idcache_get_all(conn->internal->channel_cache, &channels)) + return SILC_FSM_YIELD; + + /* Resolve channels' mode, users and topic */ + resume->channel_count = silc_list_count(channels) * 3; + silc_list_start(channels); + while ((entry = silc_list_get(channels))) { + channel = entry->context; + idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL); + if (!idp) + continue; + silc_client_command_send(client, conn, SILC_COMMAND_CMODE, + silc_client_resume_continue, conn, 1, + 1, silc_buffer_data(idp), + silc_buffer_len(idp)); + silc_client_command_send(client, conn, SILC_COMMAND_USERS, + silc_client_resume_continue, conn, 1, + 1, silc_buffer_data(idp), + silc_buffer_len(idp)); + silc_client_command_send(client, conn, SILC_COMMAND_TOPIC, + silc_client_resume_continue, conn, 1, + 1, silc_buffer_data(idp), + silc_buffer_len(idp)); + silc_buffer_free(idp); } - silc_hash_table_list_reset(&htl); + + return SILC_FSM_WAIT; +} + +/* Resuming completed */ + +SILC_FSM_STATE(silc_client_st_resume_completed) +{ + SilcClientConnection conn = fsm_context; + SilcClient client = conn->client; + SilcClientResumeSession resume = state_context; + SilcIDCacheEntry entry; + SilcChannelEntry channel; + SilcList channels; + + if (conn->internal->disconnected) { + /** Disconnected */ + silc_fsm_next(fsm, silc_client_st_resume_error); + return SILC_FSM_CONTINUE; + } + + if (resume->channel_count > 0) { + resume->channel_count--; + if (resume->channel_count) + return SILC_FSM_WAIT; + } + + SILC_LOG_DEBUG(("Resuming completed")); + + /* Issue IDENTIFY command for itself to get resolved hostname + correctly from server. */ + silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, + silc_client_command_called_dummy, NULL, + 1, 5, silc_buffer_data(conn->internal->local_idp), + silc_buffer_len(conn->internal->local_idp)); + + /* Issue INFO command to fetch the real server name and server + information and other stuff. */ + silc_client_command_send(client, conn, SILC_COMMAND_INFO, + silc_client_command_called_dummy, NULL, + 1, 2, silc_buffer_data(conn->internal->remote_idp), + silc_buffer_len(conn->internal->remote_idp)); + + /* Call connection callback. We have now resumed to SILC network. */ + conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL, + conn->callback_context); + + /* Call UMODE command reply. */ + if (conn->local_entry->mode) + silc_client_resume_command_callback(client, conn, SILC_COMMAND_UMODE, + conn->local_entry->mode); + + /* Call NICK command reply. */ + silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK, + conn->local_entry, + conn->local_entry->nickname, + &conn->local_entry->id); + + /* Call JOIN command replies for all joined channel */ + if (silc_idcache_get_all(conn->internal->channel_cache, &channels)) { + silc_list_start(channels); + while ((entry = silc_list_get(channels))) { + SilcHashTableList htl; + const char *cipher, *hmac; + + channel = entry->context; + cipher = (channel->internal.send_key ? + silc_cipher_get_name(channel->internal.send_key) : NULL); + hmac = (channel->internal.hmac ? + silc_hmac_get_name(channel->internal.hmac) : NULL); + silc_hash_table_list(channel->user_list, &htl); + silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN, + channel->channel_name, channel, + channel->mode, &htl, channel->topic, + cipher, hmac, channel->founder_key, + channel->channel_pubkeys, + channel->user_limit); + silc_hash_table_list_reset(&htl); + } + } + + conn->internal->registering = FALSE; + silc_schedule_task_del_by_all(conn->internal->schedule, 0, + silc_client_connect_timeout, conn); + silc_free(resume->nickname); + silc_free(resume); + silc_async_free(conn->internal->cop); + conn->internal->cop = NULL; return SILC_FSM_FINISH; } +/* Error resuming to network */ + SILC_FSM_STATE(silc_client_st_resume_error) { - /* XXX */ - /* Close connection */ + SilcClientConnection conn = fsm_context; + SilcClientResumeSession resume = state_context; + + if (conn->internal->disconnected) { + if (resume) { + silc_free(resume->nickname); + silc_free(resume); + } + return SILC_FSM_FINISH; + } + + SILC_LOG_DEBUG(("Error resuming to network")); + + /* Signal to close connection */ + conn->internal->status = SILC_CLIENT_CONN_ERROR; + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event); + } + + silc_schedule_task_del_by_all(conn->internal->schedule, 0, + silc_client_connect_timeout, conn); + + if (resume) { + silc_free(resume->nickname); + silc_free(resume); + } return SILC_FSM_FINISH; } @@ -470,11 +668,14 @@ SilcBuffer silc_client_get_detach_data(SilcClient client, SilcBuffer detach; SilcHashTableList htl; SilcChannelUser chu; + unsigned char id[64]; + SilcUInt32 id_len; int ret, ch_count; SILC_LOG_DEBUG(("Creating detachment data")); ch_count = silc_hash_table_count(conn->local_entry->channels); + silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len); /* Save the nickname, Client ID and user mode in SILC network */ detach = silc_buffer_alloc(0); @@ -486,12 +687,8 @@ SilcBuffer silc_client_get_detach_data(SilcClient client, SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)), SILC_STR_DATA(conn->local_entry->nickname, strlen(conn->local_entry->nickname)), - SILC_STR_UI_SHORT(silc_buffer_len(conn->internal-> - local_idp)), - SILC_STR_DATA(silc_buffer_data(conn->internal-> - local_idp), - silc_buffer_len(conn->internal-> - local_idp)), + SILC_STR_UI_SHORT(id_len), + SILC_STR_DATA(id, id_len), SILC_STR_UI_INT(conn->local_entry->mode), SILC_STR_UI_INT(ch_count), SILC_STR_END); @@ -517,7 +714,6 @@ SilcBuffer silc_client_get_detach_data(SilcClient client, SILC_STR_DATA(chid, chid_len), SILC_STR_UI_INT(chu->channel->mode), SILC_STR_END); - silc_free(chid); } silc_hash_table_list_reset(&htl);