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;
32 SilcUInt32 channel_count;
33 } *SilcClientResumeSession;
35 /************************ Static utility functions **************************/
37 /* Command callback. Nothing interesting to do here. */
40 silc_client_register_command_called(SilcClient client,
41 SilcClientConnection conn,
51 /* Continues resuming after resolving. Continue after last reply. */
54 silc_client_resume_continue(SilcClient client,
55 SilcClientConnection conn,
62 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END ||
63 SILC_STATUS_IS_ERROR(status)) {
64 silc_fsm_continue(&conn->internal->event_thread);
71 /* Function used to call command replies back to application in resuming. */
74 silc_client_resume_command_callback(SilcClient client,
75 SilcClientConnection conn,
76 SilcCommand command, ...)
79 va_start(ap, command);
80 client->internal->ops->command_reply(client, conn, command,
81 SILC_STATUS_OK, SILC_STATUS_OK, ap);
86 /****************************** NEW_ID packet *******************************/
88 /* Received new ID packet from server during registering to SILC network */
90 SILC_FSM_STATE(silc_client_new_id)
92 SilcClientConnection conn = fsm_context;
93 SilcClient client = conn->client;
94 SilcPacket packet = state_context;
100 SILC_LOG_DEBUG(("New ID received from server"));
102 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
103 silc_buffer_len(&packet->buffer), &id))
106 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
109 /* Create local client entry */
110 conn->local_entry = silc_client_add_client(client, conn,
115 if (!conn->local_entry)
118 /* Save the ID. Take reference to conn->local_id. */
119 conn->local_id = &conn->local_entry->id;
120 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
123 if (packet->src_id_len) {
124 conn->internal->remote_idp =
125 silc_id_payload_encode_data(packet->src_id,
127 packet->src_id_type);
128 if (!conn->internal->remote_idp)
130 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
131 silc_buffer_len(conn->internal->remote_idp),
135 /* Set IDs to the packet stream */
136 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
137 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
139 /* Signal connection that new ID was received so it can continue
140 with the registering. */
141 if (conn->internal->registering)
142 silc_fsm_continue_sync(&conn->internal->event_thread);
145 /** Packet processed */
146 silc_packet_free(packet);
147 return SILC_FSM_FINISH;
151 /************************ Register to SILC network **************************/
153 /* Register to network */
155 SILC_FSM_STATE(silc_client_st_register)
157 SilcClientConnection conn = fsm_context;
158 SilcClient client = conn->client;
161 SILC_LOG_DEBUG(("Register to network"));
163 /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
164 if (conn->internal->remote_version >= 13)
165 nick = (conn->internal->params.nickname ?
166 conn->internal->params.nickname : client->username);
168 /* Send NEW_CLIENT packet to register to network */
169 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
170 SILC_STR_UI_SHORT(strlen(client->username)),
171 SILC_STR_DATA(client->username,
172 strlen(client->username)),
173 SILC_STR_UI_SHORT(strlen(client->realname)),
174 SILC_STR_DATA(client->realname,
175 strlen(client->realname)),
176 SILC_STR_UI_SHORT(nick ? strlen(nick) : 0),
177 SILC_STR_DATA(nick, nick ? strlen(nick) : 0),
179 /** Error sending packet */
180 silc_fsm_next(fsm, silc_client_st_register_error);
181 return SILC_FSM_CONTINUE;
184 /** Wait for new ID */
185 conn->internal->registering = TRUE;
186 silc_fsm_next_later(fsm, silc_client_st_register_complete,
187 conn->internal->retry_timer, 0);
188 return SILC_FSM_WAIT;
191 /* Wait for NEW_ID packet to arrive */
193 SILC_FSM_STATE(silc_client_st_register_complete)
195 SilcClientConnection conn = fsm_context;
196 SilcClient client = conn->client;
198 if (conn->internal->disconnected) {
200 silc_fsm_next(fsm, silc_client_st_register_error);
201 return SILC_FSM_CONTINUE;
204 if (!conn->local_id) {
205 if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
206 /** Timeout, ID not received */
207 conn->internal->registering = FALSE;
208 conn->internal->retry_count = 0;
209 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
210 silc_fsm_next(fsm, silc_client_st_register_error);
211 return SILC_FSM_CONTINUE;
214 /** Resend registering packet */
215 silc_fsm_next(fsm, silc_client_st_register);
216 conn->internal->retry_timer = ((conn->internal->retry_timer *
217 SILC_CLIENT_RETRY_MUL) +
218 (silc_rng_get_rn16(client->rng) %
219 SILC_CLIENT_RETRY_RAND));
220 return SILC_FSM_CONTINUE;
223 SILC_LOG_DEBUG(("Registered to network"));
225 /* Issue IDENTIFY command for itself to get resolved hostname
226 correctly from server. */
227 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
228 silc_client_register_command_called, NULL,
229 1, 5, silc_buffer_data(conn->internal->local_idp),
230 silc_buffer_len(conn->internal->local_idp));
232 /* With SILC protocol version 1.2 call NICK command if the nickname was
233 set by the application. */
234 if (conn->internal->params.nickname && conn->internal->remote_version < 13 &&
235 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
236 silc_client_command_call(client, conn, NULL,
237 "NICK", conn->internal->params.nickname, NULL);
239 /* Issue INFO command to fetch the real server name and server
240 information and other stuff. */
241 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
242 silc_client_register_command_called, NULL,
243 1, 2, silc_buffer_data(conn->internal->remote_idp),
244 silc_buffer_len(conn->internal->remote_idp));
246 /* Call connection callback. We are now inside SILC network. */
247 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
248 conn->callback_context);
250 conn->internal->registering = FALSE;
251 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
252 silc_client_connect_timeout, conn);
254 return SILC_FSM_FINISH;
257 /* Error registering to network */
259 SILC_FSM_STATE(silc_client_st_register_error)
261 SilcClientConnection conn = fsm_context;
263 SILC_LOG_DEBUG(("Error registering to network"));
265 /* Signal to close connection */
266 conn->internal->status = SILC_CLIENT_CONN_ERROR;
267 if (!conn->internal->disconnected) {
268 conn->internal->disconnected = TRUE;
269 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
272 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
273 silc_client_connect_timeout, conn);
275 return SILC_FSM_FINISH;
278 /************************* Resume detached session **************************/
280 /* Resume detached session */
282 SILC_FSM_STATE(silc_client_st_resume)
284 SilcClientConnection conn = fsm_context;
285 SilcClient client = conn->client;
286 SilcClientResumeSession resume;
290 SilcClientID client_id;
293 SILC_LOG_DEBUG(("Resuming detached session"));
295 resume = silc_calloc(1, sizeof(*resume));
298 silc_fsm_next(fsm, silc_client_st_resume_error);
299 return SILC_FSM_CONTINUE;
301 silc_fsm_set_state_context(fsm, resume);
303 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
304 conn->internal->params.detach_data_len);
305 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
306 silc_buffer_len(&resume->detach));
308 /* Take the old client ID from the detachment data */
309 ret = silc_buffer_unformat(&resume->detach,
311 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
313 SILC_STR_UI16_NSTRING(&id, &id_len),
314 SILC_STR_UI_INT(NULL),
315 SILC_STR_UI_INT(&resume->channel_count),
318 /** Malformed detach data */
319 SILC_LOG_DEBUG(("Malformed detachment data"));
320 silc_fsm_next(fsm, silc_client_st_resume_error);
321 return SILC_FSM_CONTINUE;
324 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
325 sizeof(client_id))) {
327 SILC_LOG_DEBUG(("Malformed 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,
337 &client_id, SILC_ID_CLIENT);
340 silc_fsm_next(fsm, silc_client_st_resume_error);
341 return SILC_FSM_CONTINUE;
344 /* Send RESUME_CLIENT packet to resume to network */
345 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
346 SILC_STR_UI_SHORT(id_len),
347 SILC_STR_DATA(id, id_len),
348 SILC_STR_DATA(silc_buffer_data(auth),
349 silc_buffer_len(auth)),
351 /** Error sending packet */
352 SILC_LOG_DEBUG(("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->internal->disconnected) {
376 silc_fsm_next(fsm, silc_client_st_resume_error);
377 return SILC_FSM_CONTINUE;
380 if (!conn->local_id) {
381 /** Timeout, ID not received */
382 conn->internal->registering = FALSE;
383 silc_fsm_next(fsm, silc_client_st_resume_error);
384 return SILC_FSM_CONTINUE;
387 /** Wait for channels */
388 silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
390 /* Change our nickname */
391 silc_client_change_nickname(client, conn, conn->local_entry,
392 resume->nickname, NULL, NULL, 0);
394 /* Send UMODE command to get our own user mode in the network */
395 SILC_LOG_DEBUG(("Resolving user mode"));
396 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
397 silc_client_register_command_called, NULL,
398 1, 1, silc_buffer_data(conn->internal->local_idp),
399 silc_buffer_len(conn->internal->local_idp));
401 if (!resume->channel_count)
402 return SILC_FSM_YIELD;
404 /* Send IDENTIFY command for all channels we know about. These are the
405 channels we've joined to according our detachment data. */
406 for (i = 0; i < resume->channel_count; i++) {
407 SilcChannelEntry channel;
411 SilcChannelID channel_id;
414 if (silc_buffer_unformat(&resume->detach,
416 SILC_STR_UI16_NSTRING(&name, NULL),
417 SILC_STR_UI16_NSTRING(&chid, &chid_len),
418 SILC_STR_UI_INT(NULL),
422 if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id,
425 idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
429 /* Add the channel to cache */
430 channel = silc_client_get_channel_by_id(client, conn, &channel_id);
432 silc_client_add_channel(client, conn, name, 0, &channel_id);
434 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
435 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
437 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
439 res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
440 res_argv_types[res_argc] = res_argc + 5;
442 silc_buffer_free(idp);
445 /* Send IDENTIFY command */
446 SILC_LOG_DEBUG(("Resolving joined channels"));
447 silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
448 silc_client_resume_continue, conn,
449 res_argc, res_argv, res_argv_lens,
452 for (i = 0; i < resume->channel_count; i++)
453 silc_free(res_argv[i]);
455 silc_free(res_argv_lens);
456 silc_free(res_argv_types);
458 return SILC_FSM_WAIT;
461 /* Resolve joined channel modes, users and topics. */
463 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
465 SilcClientConnection conn = fsm_context;
466 SilcClient client = conn->client;
467 SilcClientResumeSession resume = state_context;
468 SilcIDCacheEntry entry;
469 SilcChannelEntry channel;
473 if (conn->internal->disconnected) {
475 silc_fsm_next(fsm, silc_client_st_resume_error);
476 return SILC_FSM_CONTINUE;
479 SILC_LOG_DEBUG(("Resolving channel details"));
481 /** Wait for channel modes */
482 silc_fsm_next(fsm, silc_client_st_resume_completed);
484 if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
485 return SILC_FSM_YIELD;
487 /* Resolve channels' mode, users and topic */
488 resume->channel_count = silc_list_count(channels) * 3;
489 silc_list_start(channels);
490 while ((entry = silc_list_get(channels))) {
491 channel = entry->context;
492 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
496 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
497 silc_client_resume_continue, conn, 1,
498 1, silc_buffer_data(idp),
499 silc_buffer_len(idp));
500 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
501 silc_client_resume_continue, conn, 1,
502 1, silc_buffer_data(idp),
503 silc_buffer_len(idp));
504 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
505 silc_client_resume_continue, conn, 1,
506 1, silc_buffer_data(idp),
507 silc_buffer_len(idp));
508 silc_buffer_free(idp);
511 return SILC_FSM_WAIT;
514 /* Resuming completed */
516 SILC_FSM_STATE(silc_client_st_resume_completed)
518 SilcClientConnection conn = fsm_context;
519 SilcClient client = conn->client;
520 SilcClientResumeSession resume = state_context;
521 SilcIDCacheEntry entry;
522 SilcChannelEntry channel;
525 if (conn->internal->disconnected) {
527 silc_fsm_next(fsm, silc_client_st_resume_error);
528 return SILC_FSM_CONTINUE;
531 if (resume->channel_count > 0) {
532 resume->channel_count--;
533 if (resume->channel_count)
534 return SILC_FSM_WAIT;
537 SILC_LOG_DEBUG(("Resuming completed"));
539 /* Issue IDENTIFY command for itself to get resolved hostname
540 correctly from server. */
541 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
542 silc_client_register_command_called, NULL,
543 1, 5, silc_buffer_data(conn->internal->local_idp),
544 silc_buffer_len(conn->internal->local_idp));
546 /* Issue INFO command to fetch the real server name and server
547 information and other stuff. */
548 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
549 silc_client_register_command_called, NULL,
550 1, 2, silc_buffer_data(conn->internal->remote_idp),
551 silc_buffer_len(conn->internal->remote_idp));
553 /* Call connection callback. We have now resumed to SILC network. */
554 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL,
555 conn->callback_context);
557 /* Call UMODE command reply. */
558 if (conn->local_entry->mode)
559 silc_client_resume_command_callback(client, conn, SILC_COMMAND_UMODE,
560 conn->local_entry->mode);
562 /* Call NICK command reply. */
563 silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK,
565 conn->local_entry->nickname,
566 &conn->local_entry->id);
568 /* Call JOIN command replies for all joined channel */
569 silc_idcache_get_all(conn->internal->channel_cache, &channels);
570 silc_list_start(channels);
571 while ((entry = silc_list_get(channels))) {
572 SilcHashTableList htl;
573 const char *cipher, *hmac;
575 channel = entry->context;
576 cipher = (channel->internal.send_key ?
577 silc_cipher_get_name(channel->internal.send_key) : NULL);
578 hmac = (channel->internal.hmac ?
579 silc_hmac_get_name(channel->internal.hmac) : NULL);
580 silc_hash_table_list(channel->user_list, &htl);
581 silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
582 channel->channel_name, channel,
583 channel->mode, &htl, channel->topic,
584 cipher, hmac, channel->founder_key,
585 channel->channel_pubkeys,
586 channel->user_limit);
587 silc_hash_table_list_reset(&htl);
590 conn->internal->registering = FALSE;
591 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
592 silc_client_connect_timeout, conn);
593 silc_free(resume->nickname);
596 return SILC_FSM_FINISH;
599 /* Error resuming to network */
601 SILC_FSM_STATE(silc_client_st_resume_error)
603 SilcClientConnection conn = fsm_context;
604 SilcClientResumeSession resume = state_context;
606 if (conn->internal->disconnected) {
608 silc_free(resume->nickname);
611 return SILC_FSM_FINISH;
614 SILC_LOG_DEBUG(("Error resuming to network"));
616 /* Signal to close connection */
617 conn->internal->status = SILC_CLIENT_CONN_ERROR;
618 if (!conn->internal->disconnected) {
619 conn->internal->disconnected = TRUE;
620 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
623 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
624 silc_client_connect_timeout, conn);
627 silc_free(resume->nickname);
631 return SILC_FSM_FINISH;
634 /* Generates the session detachment data. This data can be used later
635 to resume back to the server. */
637 SilcBuffer silc_client_get_detach_data(SilcClient client,
638 SilcClientConnection conn)
641 SilcHashTableList htl;
643 unsigned char id[64];
647 SILC_LOG_DEBUG(("Creating detachment data"));
649 ch_count = silc_hash_table_count(conn->local_entry->channels);
650 silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len);
652 /* Save the nickname, Client ID and user mode in SILC network */
653 detach = silc_buffer_alloc(0);
657 silc_buffer_format(detach,
659 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
660 SILC_STR_DATA(conn->local_entry->nickname,
661 strlen(conn->local_entry->nickname)),
662 SILC_STR_UI_SHORT(id_len),
663 SILC_STR_DATA(id, id_len),
664 SILC_STR_UI_INT(conn->local_entry->mode),
665 SILC_STR_UI_INT(ch_count),
668 silc_buffer_free(detach);
672 /* Save all joined channels */
673 silc_hash_table_list(conn->local_entry->channels, &htl);
674 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
675 unsigned char chid[32];
678 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
680 silc_buffer_format(detach,
682 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
683 SILC_STR_DATA(chu->channel->channel_name,
684 strlen(chu->channel->channel_name)),
685 SILC_STR_UI_SHORT(chid_len),
686 SILC_STR_DATA(chid, chid_len),
687 SILC_STR_UI_INT(chu->channel->mode),
690 silc_hash_table_list_reset(&htl);
692 silc_buffer_start(detach);
693 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
694 silc_buffer_len(detach));