5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcclient.h"
22 #include "client_internal.h"
24 /************************** Types and definitions ***************************/
26 /* Resume session context */
29 SilcClientConnection conn;
30 SilcBufferStruct detach;
32 SilcClientID client_id;
33 SilcUInt32 channel_count;
34 SilcUInt32 *cmd_idents;
35 SilcUInt32 cmd_idents_count;
37 } *SilcClientResumeSession;
39 /************************ Static utility functions **************************/
41 /* Command callback. Nothing interesting to do here. */
44 silc_client_register_command_called(SilcClient client,
45 SilcClientConnection conn,
55 /* Continues resuming after resolving. Continue after last reply. */
58 silc_client_resume_continue(SilcClient client,
59 SilcClientConnection conn,
66 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END ||
67 SILC_STATUS_IS_ERROR(status)) {
68 silc_fsm_continue(&conn->internal->event_thread);
75 /****************************** NEW_ID packet *******************************/
77 /* Received new ID packet from server during registering to SILC network */
79 SILC_FSM_STATE(silc_client_new_id)
81 SilcClientConnection conn = fsm_context;
82 SilcClient client = conn->client;
83 SilcPacket packet = state_context;
89 SILC_LOG_DEBUG(("New ID received from server"));
91 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
92 silc_buffer_len(&packet->buffer), &id))
95 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
98 /* Create local client entry */
99 conn->local_entry = silc_client_add_client(client, conn,
104 if (!conn->local_entry)
108 conn->local_id = &conn->local_entry->id;
109 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
111 /* Save cache entry */
112 silc_mutex_lock(conn->internal->lock);
113 if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
115 &conn->internal->local_entry)) {
116 silc_mutex_unlock(conn->internal->lock);
119 silc_mutex_unlock(conn->internal->lock);
122 if (packet->src_id_len) {
123 conn->internal->remote_idp =
124 silc_id_payload_encode_data(packet->src_id,
126 packet->src_id_type);
127 if (!conn->internal->remote_idp)
129 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
130 silc_buffer_len(conn->internal->remote_idp),
134 /* Set IDs to the packet stream */
135 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
136 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
138 /* Signal connection that new ID was received so it can continue
139 with the registering. */
140 if (conn->internal->registering)
141 silc_fsm_continue_sync(&conn->internal->event_thread);
144 /** Packet processed */
145 silc_packet_free(packet);
146 return SILC_FSM_FINISH;
150 /************************ Register to SILC network **************************/
152 /* Register to network */
154 SILC_FSM_STATE(silc_client_st_register)
156 SilcClientConnection conn = fsm_context;
157 SilcClient client = conn->client;
159 SILC_LOG_DEBUG(("Register to network"));
161 /* Send NEW_CLIENT packet to register to network */
162 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
163 SILC_STR_UI_SHORT(strlen(client->username)),
164 SILC_STR_DATA(client->username,
165 strlen(client->username)),
166 SILC_STR_UI_SHORT(strlen(client->realname)),
167 SILC_STR_DATA(client->realname,
168 strlen(client->realname)),
170 /** Error sending packet */
171 silc_fsm_next(fsm, silc_client_st_register_error);
172 return SILC_FSM_CONTINUE;
175 /** Wait for new ID */
176 conn->internal->registering = TRUE;
177 silc_fsm_next_later(fsm, silc_client_st_register_complete,
178 conn->internal->retry_timer, 0);
179 return SILC_FSM_WAIT;
182 /* Wait for NEW_ID packet to arrive */
184 SILC_FSM_STATE(silc_client_st_register_complete)
186 SilcClientConnection conn = fsm_context;
187 SilcClient client = conn->client;
189 if (conn->internal->aborted) {
191 silc_fsm_next(fsm, silc_client_st_register_error);
192 return SILC_FSM_CONTINUE;
195 if (conn->internal->disconnected) {
197 silc_fsm_next(fsm, silc_client_st_register_error);
198 return SILC_FSM_CONTINUE;
201 if (!conn->local_id) {
202 if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
203 /** Timeout, ID not received */
204 conn->internal->registering = FALSE;
205 conn->internal->retry_count = 0;
206 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
207 silc_fsm_next(fsm, silc_client_st_register_error);
208 return SILC_FSM_CONTINUE;
211 /** Resend registering packet */
212 silc_fsm_next(fsm, silc_client_st_register);
213 conn->internal->retry_timer = ((conn->internal->retry_timer *
214 SILC_CLIENT_RETRY_MUL) +
215 (silc_rng_get_rn16(client->rng) %
216 SILC_CLIENT_RETRY_RAND));
217 return SILC_FSM_CONTINUE;
220 SILC_LOG_DEBUG(("Registered to network"));
222 /* Issue IDENTIFY command for itself to get resolved hostname
223 correctly from server. */
224 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
225 silc_client_register_command_called, NULL,
226 1, 5, silc_buffer_data(conn->internal->local_idp),
227 silc_buffer_len(conn->internal->local_idp));
229 /* Call NICK command if the nickname was set by the application (and is
230 not same as the username). */
231 if (conn->internal->params.nickname &&
232 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
233 silc_client_command_call(client, conn, NULL,
234 "NICK", conn->internal->params.nickname, NULL);
236 /* Issue INFO command to fetch the real server name and server
237 information and other stuff. */
238 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
239 silc_client_register_command_called, NULL,
240 1, 2, silc_buffer_data(conn->internal->remote_idp),
241 silc_buffer_len(conn->internal->remote_idp));
243 /* Call connection callback. We are now inside SILC network. */
244 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
245 conn->callback_context);
247 conn->internal->registering = FALSE;
248 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
249 silc_client_connect_timeout, conn);
251 return SILC_FSM_FINISH;
254 /* Error registering to network */
256 SILC_FSM_STATE(silc_client_st_register_error)
258 SilcClientConnection conn = fsm_context;
259 SilcClient client = conn->client;
261 SILC_LOG_DEBUG(("Error registering to network"));
263 /* Signal to close connection */
264 if (!conn->internal->disconnected) {
265 conn->internal->disconnected = TRUE;
266 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
269 /* Call connect callback */
270 if (conn->internal->callback_called)
271 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
272 conn->callback_context);
273 conn->internal->callback_called = TRUE;
275 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
276 silc_client_connect_timeout, conn);
278 return SILC_FSM_FINISH;
281 /************************* Resume detached session **************************/
283 /* Resume detached session */
285 SILC_FSM_STATE(silc_client_st_resume)
287 SilcClientConnection conn = fsm_context;
288 SilcClient client = conn->client;
289 SilcClientResumeSession resume;
295 SILC_LOG_DEBUG(("Resuming detached session"));
297 resume = silc_calloc(1, sizeof(*resume));
300 silc_fsm_next(fsm, silc_client_st_resume_error);
301 return SILC_FSM_CONTINUE;
303 silc_fsm_set_state_context(fsm, resume);
305 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
306 conn->internal->params.detach_data_len);
307 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
308 silc_buffer_len(&resume->detach));
310 /* Take the old client ID from the detachment data */
311 ret = silc_buffer_unformat(&resume->detach,
313 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
315 SILC_STR_UI16_NSTRING(&id, &id_len),
316 SILC_STR_UI_INT(NULL),
317 SILC_STR_UI_INT(&resume->channel_count),
320 /** Malformed detach data */
321 silc_fsm_next(fsm, silc_client_st_resume_error);
322 return SILC_FSM_CONTINUE;
325 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id,
326 sizeof(resume->client_id))) {
328 silc_fsm_next(fsm, silc_client_st_resume_error);
329 return SILC_FSM_CONTINUE;
332 /* Generate authentication data that server will verify */
333 auth = silc_auth_public_key_auth_generate(conn->public_key,
336 conn->internal->hash,
341 silc_fsm_next(fsm, silc_client_st_resume_error);
342 return SILC_FSM_CONTINUE;
345 /* Send RESUME_CLIENT packet to resume to network */
346 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
347 SILC_STR_UI_SHORT(id_len),
348 SILC_STR_DATA(id, id_len),
349 SILC_STR_DATA(silc_buffer_data(auth),
350 silc_buffer_len(auth)),
352 /** Error sending packet */
353 silc_fsm_next(fsm, silc_client_st_resume_error);
354 return SILC_FSM_CONTINUE;
357 /** Wait for new ID */
358 conn->internal->registering = TRUE;
359 silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
360 return SILC_FSM_WAIT;
363 /* Resolve the old session information, user mode and joined channels. */
365 SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
367 SilcClientConnection conn = fsm_context;
368 SilcClient client = conn->client;
369 SilcClientResumeSession resume = state_context;
370 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
371 unsigned char **res_argv = NULL;
374 if (!conn->local_id) {
375 /** Timeout, ID not received */
376 conn->internal->registering = FALSE;
377 silc_fsm_next(fsm, silc_client_st_resume_error);
378 return SILC_FSM_CONTINUE;
381 /* First, send UMODE command to get our own user mode in the network */
382 SILC_LOG_DEBUG(("Resolving user mode"));
383 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
384 silc_client_register_command_called, NULL,
385 1, 1, silc_buffer_data(conn->internal->local_idp),
386 silc_buffer_len(conn->internal->local_idp));
388 /* Second, send IDENTIFY command for all channels we know about. These
389 are the channels we've joined to according our detachment data. */
390 for (i = 0; i < resume->channel_count; i++) {
391 SilcChannelID channel_id;
396 if (silc_buffer_unformat(&resume->detach,
398 SILC_STR_UI16_NSTRING(NULL, NULL),
399 SILC_STR_UI16_NSTRING(&chid, &chid_len),
400 SILC_STR_UI_INT(NULL),
404 idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
407 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
408 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
410 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
412 res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
413 res_argv_types[res_argc] = res_argc + 5;
415 silc_buffer_free(idp);
418 /* Send IDENTIFY command */
419 SILC_LOG_DEBUG(("Resolving joined channels"));
420 silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
421 silc_client_resume_continue, conn,
422 res_argc, res_argv, res_argv_lens,
425 for (i = 0; i < resume->channel_count; i++)
426 silc_free(res_argv[i]);
428 silc_free(res_argv_lens);
429 silc_free(res_argv_types);
431 /** Wait for channels */
432 silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
433 return SILC_FSM_WAIT;
436 /* Resolve joined channel modes. */
438 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
440 SilcClientConnection conn = fsm_context;
441 SilcClientResumeSession resume = state_context;
442 SilcHashTableList htl;
445 SILC_LOG_DEBUG(("Resolving joined channel modes"));
447 silc_hash_table_list(conn->local_entry->channels, &htl);
448 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
451 silc_hash_table_list_reset(&htl);
453 return SILC_FSM_FINISH;
456 SILC_FSM_STATE(silc_client_st_resume_error)
459 /* Close connection */
461 return SILC_FSM_FINISH;
464 /* Generates the session detachment data. This data can be used later
465 to resume back to the server. */
467 SilcBuffer silc_client_get_detach_data(SilcClient client,
468 SilcClientConnection conn)
471 SilcHashTableList htl;
475 SILC_LOG_DEBUG(("Creating detachment data"));
477 ch_count = silc_hash_table_count(conn->local_entry->channels);
479 /* Save the nickname, Client ID and user mode in SILC network */
480 detach = silc_buffer_alloc(0);
484 silc_buffer_format(detach,
486 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
487 SILC_STR_DATA(conn->local_entry->nickname,
488 strlen(conn->local_entry->nickname)),
489 SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
491 SILC_STR_DATA(silc_buffer_data(conn->internal->
493 silc_buffer_len(conn->internal->
495 SILC_STR_UI_INT(conn->local_entry->mode),
496 SILC_STR_UI_INT(ch_count),
499 silc_buffer_free(detach);
503 /* Save all joined channels */
504 silc_hash_table_list(conn->local_entry->channels, &htl);
505 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
506 unsigned char chid[32];
509 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
511 silc_buffer_format(detach,
513 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
514 SILC_STR_DATA(chu->channel->channel_name,
515 strlen(chu->channel->channel_name)),
516 SILC_STR_UI_SHORT(chid_len),
517 SILC_STR_DATA(chid, chid_len),
518 SILC_STR_UI_INT(chu->channel->mode),
522 silc_hash_table_list_reset(&htl);
524 silc_buffer_start(detach);
525 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
526 silc_buffer_len(detach));