5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2006 - 2007 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;
35 SilcUInt32 channel_count;
36 } *SilcClientResumeSession;
38 /************************ Static utility functions **************************/
40 /* Continues resuming after resolving. Continue after last reply. */
43 silc_client_resume_continue(SilcClient client,
44 SilcClientConnection conn,
51 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END ||
52 SILC_STATUS_IS_ERROR(status)) {
53 silc_fsm_continue(&conn->internal->event_thread);
60 /* Function used to call command replies back to application in resuming. */
63 silc_client_resume_command_callback(SilcClient client,
64 SilcClientConnection conn,
65 SilcCommand command, ...)
68 va_start(ap, command);
69 client->internal->ops->command_reply(client, conn, command,
70 SILC_STATUS_OK, SILC_STATUS_OK, ap);
74 /* Resume authentication data generation callback */
76 static void silc_client_resume_auth_generated(const SilcBuffer data,
79 SilcClientConnection conn = context;
80 SilcClientResumeSession resume =
81 silc_fsm_get_state_context(&conn->internal->event_thread);
84 silc_fsm_next(&conn->internal->event_thread, silc_client_st_resume_error);
86 resume->auth = silc_buffer_copy(data);
88 SILC_FSM_CALL_CONTINUE_SYNC(&conn->internal->event_thread);
92 /****************************** NEW_ID packet *******************************/
94 /* Received new ID packet from server during registering to SILC network */
96 SILC_FSM_STATE(silc_client_new_id)
98 SilcClientConnection conn = fsm_context;
99 SilcClient client = conn->client;
100 SilcPacket packet = state_context;
107 SILC_LOG_DEBUG(("New ID received from server"));
109 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
110 silc_buffer_len(&packet->buffer), &id))
113 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
116 /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
117 if (conn->internal->remote_version >= 13)
118 nick = (conn->internal->params.nickname ?
119 conn->internal->params.nickname : client->username);
121 nick = client->username;
123 /* Create local client entry */
124 conn->local_entry = silc_client_add_client(client, conn, nick,
128 if (!conn->local_entry)
131 /* Save the ID. Take reference to conn->local_id. */
132 conn->local_id = &conn->local_entry->id;
133 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
136 if (packet->src_id_len) {
137 conn->internal->remote_idp =
138 silc_id_payload_encode_data(packet->src_id,
140 packet->src_id_type);
141 if (!conn->internal->remote_idp)
143 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
144 silc_buffer_len(conn->internal->remote_idp),
148 /* Set IDs to the packet stream */
149 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
150 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
152 /* Signal connection that new ID was received so it can continue
153 with the registering. */
154 if (conn->internal->registering)
155 silc_fsm_continue_sync(&conn->internal->event_thread);
158 /** Packet processed */
159 silc_packet_free(packet);
160 return SILC_FSM_FINISH;
164 /************************ Register to SILC network **************************/
166 /* Register to network */
168 SILC_FSM_STATE(silc_client_st_register)
170 SilcClientConnection conn = fsm_context;
171 SilcClient client = conn->client;
174 SILC_LOG_DEBUG(("Register to network"));
176 /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
177 if (conn->internal->remote_version >= 13)
178 nick = (conn->internal->params.nickname ?
179 conn->internal->params.nickname : client->username);
181 /* Send NEW_CLIENT packet to register to network */
182 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
183 SILC_STR_UI_SHORT(strlen(client->username)),
184 SILC_STR_DATA(client->username,
185 strlen(client->username)),
186 SILC_STR_UI_SHORT(strlen(client->realname)),
187 SILC_STR_DATA(client->realname,
188 strlen(client->realname)),
189 SILC_STR_UI_SHORT(nick ? strlen(nick) : 0),
190 SILC_STR_DATA(nick, nick ? strlen(nick) : 0),
192 /** Error sending packet */
193 silc_fsm_next(fsm, silc_client_st_register_error);
194 return SILC_FSM_CONTINUE;
197 /** Wait for new ID */
198 conn->internal->registering = TRUE;
199 silc_fsm_next_later(fsm, silc_client_st_register_complete,
200 conn->internal->retry_timer, 0);
201 return SILC_FSM_WAIT;
204 /* Wait for NEW_ID packet to arrive */
206 SILC_FSM_STATE(silc_client_st_register_complete)
208 SilcClientConnection conn = fsm_context;
209 SilcClient client = conn->client;
211 if (conn->internal->disconnected) {
213 silc_fsm_next(fsm, silc_client_st_register_error);
214 return SILC_FSM_CONTINUE;
217 if (!conn->local_id) {
218 if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
219 /** Timeout, ID not received */
220 conn->internal->registering = FALSE;
221 conn->internal->retry_count = 0;
222 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
223 silc_fsm_next(fsm, silc_client_st_register_error);
224 return SILC_FSM_CONTINUE;
227 /** Resend registering packet */
228 silc_fsm_next(fsm, silc_client_st_register);
229 conn->internal->retry_timer = ((conn->internal->retry_timer *
230 SILC_CLIENT_RETRY_MUL) +
231 (silc_rng_get_rn16(client->rng) %
232 SILC_CLIENT_RETRY_RAND));
233 return SILC_FSM_CONTINUE;
236 SILC_LOG_DEBUG(("Registered to network"));
238 /* Issue IDENTIFY command for itself to get resolved hostname
239 correctly from server. */
240 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
241 silc_client_command_called_dummy, NULL,
242 1, 5, silc_buffer_data(conn->internal->local_idp),
243 silc_buffer_len(conn->internal->local_idp));
245 /* With SILC protocol version 1.2 call NICK command if the nickname was
246 set by the application. */
247 if (conn->internal->params.nickname && conn->internal->remote_version < 13 &&
248 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
249 silc_client_command_call(client, conn, NULL,
250 "NICK", conn->internal->params.nickname, NULL);
252 /* Issue INFO command to fetch the real server name and server
253 information and other stuff. */
254 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
255 silc_client_command_called_dummy, NULL,
256 1, 2, silc_buffer_data(conn->internal->remote_idp),
257 silc_buffer_len(conn->internal->remote_idp));
259 /* Call connection callback. We are now inside SILC network. */
260 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
261 conn->callback_context);
263 conn->internal->registering = FALSE;
264 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
265 silc_client_connect_timeout, conn);
266 silc_async_free(conn->internal->cop);
267 conn->internal->cop = NULL;
269 return SILC_FSM_FINISH;
272 /* Error registering to network */
274 SILC_FSM_STATE(silc_client_st_register_error)
276 SilcClientConnection conn = fsm_context;
278 SILC_LOG_DEBUG(("Error registering to network"));
280 /* Signal to close connection */
281 conn->internal->status = SILC_CLIENT_CONN_ERROR;
282 if (!conn->internal->disconnected) {
283 conn->internal->disconnected = TRUE;
284 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
287 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
288 silc_client_connect_timeout, conn);
290 return SILC_FSM_FINISH;
293 /************************* Resume detached session **************************/
295 /* Resume detached session */
297 SILC_FSM_STATE(silc_client_st_resume)
299 SilcClientConnection conn = fsm_context;
300 SilcClient client = conn->client;
301 SilcClientResumeSession resume;
304 SilcClientID client_id;
307 SILC_LOG_DEBUG(("Resuming detached session"));
309 resume = silc_calloc(1, sizeof(*resume));
312 silc_fsm_next(fsm, silc_client_st_resume_error);
313 return SILC_FSM_CONTINUE;
315 silc_fsm_set_state_context(fsm, resume);
317 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
318 conn->internal->params.detach_data_len);
319 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
320 silc_buffer_len(&resume->detach));
322 /* Take the old client ID from the detachment data */
323 ret = silc_buffer_unformat(&resume->detach,
325 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
327 SILC_STR_UI16_NSTRING(&id, &id_len),
328 SILC_STR_UI_INT(NULL),
329 SILC_STR_UI_INT(&resume->channel_count),
332 /** Malformed detach data */
333 SILC_LOG_DEBUG(("Malformed detachment data"));
334 silc_fsm_next(fsm, silc_client_st_resume_error);
335 return SILC_FSM_CONTINUE;
338 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
339 sizeof(client_id))) {
341 SILC_LOG_DEBUG(("Malformed ID"));
342 silc_fsm_next(fsm, silc_client_st_resume_error);
343 return SILC_FSM_CONTINUE;
346 resume->id_len = id_len;
348 /* Generate authentication data that server will verify */
349 silc_fsm_next(fsm, silc_client_st_resume_send);
350 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
351 conn->public_key, conn->private_key,
352 client->rng, conn->internal->hash,
353 &client_id, SILC_ID_CLIENT,
354 silc_client_resume_auth_generated, conn));
358 /* Send RESUME_CLIENT packet */
360 SILC_FSM_STATE(silc_client_st_resume_send)
362 SilcClientConnection conn = fsm_context;
363 SilcClientResumeSession resume = state_context;
365 SILC_LOG_DEBUG(("Send RESUME_CLIENT packet"));
367 /* Send RESUME_CLIENT packet to resume to network */
368 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
369 SILC_STR_UI_SHORT(resume->id_len),
370 SILC_STR_DATA(resume->id, resume->id_len),
371 SILC_STR_DATA(silc_buffer_data(resume->auth),
372 silc_buffer_len(resume->auth)),
374 /** Error sending packet */
375 SILC_LOG_DEBUG(("Error sending packet"));
376 silc_fsm_next(fsm, silc_client_st_resume_error);
377 return SILC_FSM_CONTINUE;
380 /** Wait for new ID */
381 conn->internal->registering = TRUE;
382 silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
383 return SILC_FSM_WAIT;
386 /* Resolve the old session information, user mode and joined channels. */
388 SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
390 SilcClientConnection conn = fsm_context;
391 SilcClient client = conn->client;
392 SilcClientResumeSession resume = state_context;
393 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
394 unsigned char **res_argv = NULL;
397 if (conn->internal->disconnected) {
399 silc_fsm_next(fsm, silc_client_st_resume_error);
400 return SILC_FSM_CONTINUE;
403 if (!conn->local_id) {
404 /** Timeout, ID not received */
405 conn->internal->registering = FALSE;
406 silc_fsm_next(fsm, silc_client_st_resume_error);
407 return SILC_FSM_CONTINUE;
410 /** Wait for channels */
411 silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
413 /* Change our nickname */
414 silc_client_change_nickname(client, conn, conn->local_entry,
415 resume->nickname, NULL, NULL, 0);
417 /* Send UMODE command to get our own user mode in the network */
418 SILC_LOG_DEBUG(("Resolving user mode"));
419 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
420 silc_client_command_called_dummy, NULL,
421 1, 1, silc_buffer_data(conn->internal->local_idp),
422 silc_buffer_len(conn->internal->local_idp));
424 if (!resume->channel_count)
425 return SILC_FSM_YIELD;
427 /* Send IDENTIFY command for all channels we know about. These are the
428 channels we've joined to according our detachment data. */
429 for (i = 0; i < resume->channel_count; i++) {
430 SilcChannelEntry channel;
434 SilcChannelID channel_id;
437 if (silc_buffer_unformat(&resume->detach,
439 SILC_STR_UI16_NSTRING(&name, NULL),
440 SILC_STR_UI16_NSTRING(&chid, &chid_len),
441 SILC_STR_UI_INT(NULL),
445 if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id,
448 idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
452 /* Add the channel to cache */
453 channel = silc_client_get_channel_by_id(client, conn, &channel_id);
455 silc_client_add_channel(client, conn, name, 0, &channel_id);
457 silc_client_unref_channel(client, conn, channel);
459 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
460 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
462 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
464 res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
465 res_argv_types[res_argc] = res_argc + 5;
467 silc_buffer_free(idp);
470 /* Send IDENTIFY command */
471 SILC_LOG_DEBUG(("Resolving joined channels"));
472 silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
473 silc_client_resume_continue, conn,
474 res_argc, res_argv, res_argv_lens,
477 for (i = 0; i < resume->channel_count; i++)
478 silc_free(res_argv[i]);
480 silc_free(res_argv_lens);
481 silc_free(res_argv_types);
483 return SILC_FSM_WAIT;
486 /* Resolve joined channel modes, users and topics. */
488 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
490 SilcClientConnection conn = fsm_context;
491 SilcClient client = conn->client;
492 SilcClientResumeSession resume = state_context;
493 SilcIDCacheEntry entry;
494 SilcChannelEntry channel;
498 if (conn->internal->disconnected) {
500 silc_fsm_next(fsm, silc_client_st_resume_error);
501 return SILC_FSM_CONTINUE;
504 SILC_LOG_DEBUG(("Resolving channel details"));
506 /** Wait for channel modes */
507 silc_fsm_next(fsm, silc_client_st_resume_completed);
509 if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
510 return SILC_FSM_YIELD;
512 /* Resolve channels' mode, users and topic */
513 resume->channel_count = silc_list_count(channels) * 3;
514 silc_list_start(channels);
515 while ((entry = silc_list_get(channels))) {
516 channel = entry->context;
517 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
521 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
522 silc_client_resume_continue, conn, 1,
523 1, silc_buffer_data(idp),
524 silc_buffer_len(idp));
525 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
526 silc_client_resume_continue, conn, 1,
527 1, silc_buffer_data(idp),
528 silc_buffer_len(idp));
529 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
530 silc_client_resume_continue, conn, 1,
531 1, silc_buffer_data(idp),
532 silc_buffer_len(idp));
533 silc_buffer_free(idp);
536 return SILC_FSM_WAIT;
539 /* Resuming completed */
541 SILC_FSM_STATE(silc_client_st_resume_completed)
543 SilcClientConnection conn = fsm_context;
544 SilcClient client = conn->client;
545 SilcClientResumeSession resume = state_context;
546 SilcIDCacheEntry entry;
547 SilcChannelEntry channel;
550 if (conn->internal->disconnected) {
552 silc_fsm_next(fsm, silc_client_st_resume_error);
553 return SILC_FSM_CONTINUE;
556 if (resume->channel_count > 0) {
557 resume->channel_count--;
558 if (resume->channel_count)
559 return SILC_FSM_WAIT;
562 SILC_LOG_DEBUG(("Resuming completed"));
564 /* Issue IDENTIFY command for itself to get resolved hostname
565 correctly from server. */
566 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
567 silc_client_command_called_dummy, NULL,
568 1, 5, silc_buffer_data(conn->internal->local_idp),
569 silc_buffer_len(conn->internal->local_idp));
571 /* Issue INFO command to fetch the real server name and server
572 information and other stuff. */
573 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
574 silc_client_command_called_dummy, NULL,
575 1, 2, silc_buffer_data(conn->internal->remote_idp),
576 silc_buffer_len(conn->internal->remote_idp));
578 /* Call connection callback. We have now resumed to SILC network. */
579 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL,
580 conn->callback_context);
582 /* Call UMODE command reply. */
583 if (conn->local_entry->mode)
584 silc_client_resume_command_callback(client, conn, SILC_COMMAND_UMODE,
585 conn->local_entry->mode);
587 /* Call NICK command reply. */
588 silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK,
590 conn->local_entry->nickname,
591 &conn->local_entry->id);
593 /* Call JOIN command replies for all joined channel */
594 silc_idcache_get_all(conn->internal->channel_cache, &channels);
595 silc_list_start(channels);
596 while ((entry = silc_list_get(channels))) {
597 SilcHashTableList htl;
598 const char *cipher, *hmac;
600 channel = entry->context;
601 cipher = (channel->internal.send_key ?
602 silc_cipher_get_name(channel->internal.send_key) : NULL);
603 hmac = (channel->internal.hmac ?
604 silc_hmac_get_name(channel->internal.hmac) : NULL);
605 silc_hash_table_list(channel->user_list, &htl);
606 silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
607 channel->channel_name, channel,
608 channel->mode, &htl, channel->topic,
609 cipher, hmac, channel->founder_key,
610 channel->channel_pubkeys,
611 channel->user_limit);
612 silc_hash_table_list_reset(&htl);
615 conn->internal->registering = FALSE;
616 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
617 silc_client_connect_timeout, conn);
618 silc_free(resume->nickname);
620 silc_async_free(conn->internal->cop);
621 conn->internal->cop = NULL;
623 return SILC_FSM_FINISH;
626 /* Error resuming to network */
628 SILC_FSM_STATE(silc_client_st_resume_error)
630 SilcClientConnection conn = fsm_context;
631 SilcClientResumeSession resume = state_context;
633 if (conn->internal->disconnected) {
635 silc_free(resume->nickname);
638 return SILC_FSM_FINISH;
641 SILC_LOG_DEBUG(("Error resuming to network"));
643 /* Signal to close connection */
644 conn->internal->status = SILC_CLIENT_CONN_ERROR;
645 if (!conn->internal->disconnected) {
646 conn->internal->disconnected = TRUE;
647 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
650 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
651 silc_client_connect_timeout, conn);
654 silc_free(resume->nickname);
658 return SILC_FSM_FINISH;
661 /* Generates the session detachment data. This data can be used later
662 to resume back to the server. */
664 SilcBuffer silc_client_get_detach_data(SilcClient client,
665 SilcClientConnection conn)
668 SilcHashTableList htl;
670 unsigned char id[64];
674 SILC_LOG_DEBUG(("Creating detachment data"));
676 ch_count = silc_hash_table_count(conn->local_entry->channels);
677 silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len);
679 /* Save the nickname, Client ID and user mode in SILC network */
680 detach = silc_buffer_alloc(0);
684 silc_buffer_format(detach,
686 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
687 SILC_STR_DATA(conn->local_entry->nickname,
688 strlen(conn->local_entry->nickname)),
689 SILC_STR_UI_SHORT(id_len),
690 SILC_STR_DATA(id, id_len),
691 SILC_STR_UI_INT(conn->local_entry->mode),
692 SILC_STR_UI_INT(ch_count),
695 silc_buffer_free(detach);
699 /* Save all joined channels */
700 silc_hash_table_list(conn->local_entry->channels, &htl);
701 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
702 unsigned char chid[32];
705 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
707 silc_buffer_format(detach,
709 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
710 SILC_STR_DATA(chu->channel->channel_name,
711 strlen(chu->channel->channel_name)),
712 SILC_STR_UI_SHORT(chid_len),
713 SILC_STR_DATA(chid, chid_len),
714 SILC_STR_UI_INT(chu->channel->mode),
717 silc_hash_table_list_reset(&htl);
719 silc_buffer_start(detach);
720 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
721 silc_buffer_len(detach));