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 /* Continues resuming after resolving. Continue after last reply. */
40 silc_client_resume_continue(SilcClient client,
41 SilcClientConnection conn,
48 if (status == SILC_STATUS_OK || status == SILC_STATUS_LIST_END ||
49 SILC_STATUS_IS_ERROR(status)) {
50 silc_fsm_continue(&conn->internal->event_thread);
57 /* Function used to call command replies back to application in resuming. */
60 silc_client_resume_command_callback(SilcClient client,
61 SilcClientConnection conn,
62 SilcCommand command, ...)
65 va_start(ap, command);
66 client->internal->ops->command_reply(client, conn, command,
67 SILC_STATUS_OK, SILC_STATUS_OK, ap);
72 /****************************** NEW_ID packet *******************************/
74 /* Received new ID packet from server during registering to SILC network */
76 SILC_FSM_STATE(silc_client_new_id)
78 SilcClientConnection conn = fsm_context;
79 SilcClient client = conn->client;
80 SilcPacket packet = state_context;
86 SILC_LOG_DEBUG(("New ID received from server"));
88 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
89 silc_buffer_len(&packet->buffer), &id))
92 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
95 /* Create local client entry */
96 conn->local_entry = silc_client_add_client(client, conn,
101 if (!conn->local_entry)
104 /* Save the ID. Take reference to conn->local_id. */
105 conn->local_id = &conn->local_entry->id;
106 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
109 if (packet->src_id_len) {
110 conn->internal->remote_idp =
111 silc_id_payload_encode_data(packet->src_id,
113 packet->src_id_type);
114 if (!conn->internal->remote_idp)
116 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
117 silc_buffer_len(conn->internal->remote_idp),
121 /* Set IDs to the packet stream */
122 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
123 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
125 /* Signal connection that new ID was received so it can continue
126 with the registering. */
127 if (conn->internal->registering)
128 silc_fsm_continue_sync(&conn->internal->event_thread);
131 /** Packet processed */
132 silc_packet_free(packet);
133 return SILC_FSM_FINISH;
137 /************************ Register to SILC network **************************/
139 /* Register to network */
141 SILC_FSM_STATE(silc_client_st_register)
143 SilcClientConnection conn = fsm_context;
144 SilcClient client = conn->client;
147 SILC_LOG_DEBUG(("Register to network"));
149 /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
150 if (conn->internal->remote_version >= 13)
151 nick = (conn->internal->params.nickname ?
152 conn->internal->params.nickname : client->username);
154 /* Send NEW_CLIENT packet to register to network */
155 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
156 SILC_STR_UI_SHORT(strlen(client->username)),
157 SILC_STR_DATA(client->username,
158 strlen(client->username)),
159 SILC_STR_UI_SHORT(strlen(client->realname)),
160 SILC_STR_DATA(client->realname,
161 strlen(client->realname)),
162 SILC_STR_UI_SHORT(nick ? strlen(nick) : 0),
163 SILC_STR_DATA(nick, nick ? strlen(nick) : 0),
165 /** Error sending packet */
166 silc_fsm_next(fsm, silc_client_st_register_error);
167 return SILC_FSM_CONTINUE;
170 /** Wait for new ID */
171 conn->internal->registering = TRUE;
172 silc_fsm_next_later(fsm, silc_client_st_register_complete,
173 conn->internal->retry_timer, 0);
174 return SILC_FSM_WAIT;
177 /* Wait for NEW_ID packet to arrive */
179 SILC_FSM_STATE(silc_client_st_register_complete)
181 SilcClientConnection conn = fsm_context;
182 SilcClient client = conn->client;
184 if (conn->internal->disconnected) {
186 silc_fsm_next(fsm, silc_client_st_register_error);
187 return SILC_FSM_CONTINUE;
190 if (!conn->local_id) {
191 if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
192 /** Timeout, ID not received */
193 conn->internal->registering = FALSE;
194 conn->internal->retry_count = 0;
195 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
196 silc_fsm_next(fsm, silc_client_st_register_error);
197 return SILC_FSM_CONTINUE;
200 /** Resend registering packet */
201 silc_fsm_next(fsm, silc_client_st_register);
202 conn->internal->retry_timer = ((conn->internal->retry_timer *
203 SILC_CLIENT_RETRY_MUL) +
204 (silc_rng_get_rn16(client->rng) %
205 SILC_CLIENT_RETRY_RAND));
206 return SILC_FSM_CONTINUE;
209 SILC_LOG_DEBUG(("Registered to network"));
211 /* Issue IDENTIFY command for itself to get resolved hostname
212 correctly from server. */
213 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
214 silc_client_command_called_dummy, NULL,
215 1, 5, silc_buffer_data(conn->internal->local_idp),
216 silc_buffer_len(conn->internal->local_idp));
218 /* With SILC protocol version 1.2 call NICK command if the nickname was
219 set by the application. */
220 if (conn->internal->params.nickname && conn->internal->remote_version < 13 &&
221 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
222 silc_client_command_call(client, conn, NULL,
223 "NICK", conn->internal->params.nickname, NULL);
225 /* Issue INFO command to fetch the real server name and server
226 information and other stuff. */
227 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
228 silc_client_command_called_dummy, NULL,
229 1, 2, silc_buffer_data(conn->internal->remote_idp),
230 silc_buffer_len(conn->internal->remote_idp));
232 /* Call connection callback. We are now inside SILC network. */
233 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
234 conn->callback_context);
236 conn->internal->registering = FALSE;
237 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
238 silc_client_connect_timeout, conn);
239 silc_async_free(conn->internal->cop);
240 conn->internal->cop = NULL;
242 return SILC_FSM_FINISH;
245 /* Error registering to network */
247 SILC_FSM_STATE(silc_client_st_register_error)
249 SilcClientConnection conn = fsm_context;
251 SILC_LOG_DEBUG(("Error registering to network"));
253 /* Signal to close connection */
254 conn->internal->status = SILC_CLIENT_CONN_ERROR;
255 if (!conn->internal->disconnected) {
256 conn->internal->disconnected = TRUE;
257 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
260 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
261 silc_client_connect_timeout, conn);
263 return SILC_FSM_FINISH;
266 /************************* Resume detached session **************************/
268 /* Resume detached session */
270 SILC_FSM_STATE(silc_client_st_resume)
272 SilcClientConnection conn = fsm_context;
273 SilcClient client = conn->client;
274 SilcClientResumeSession resume;
278 SilcClientID client_id;
281 SILC_LOG_DEBUG(("Resuming detached session"));
283 resume = silc_calloc(1, sizeof(*resume));
286 silc_fsm_next(fsm, silc_client_st_resume_error);
287 return SILC_FSM_CONTINUE;
289 silc_fsm_set_state_context(fsm, resume);
291 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
292 conn->internal->params.detach_data_len);
293 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
294 silc_buffer_len(&resume->detach));
296 /* Take the old client ID from the detachment data */
297 ret = silc_buffer_unformat(&resume->detach,
299 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
301 SILC_STR_UI16_NSTRING(&id, &id_len),
302 SILC_STR_UI_INT(NULL),
303 SILC_STR_UI_INT(&resume->channel_count),
306 /** Malformed detach data */
307 SILC_LOG_DEBUG(("Malformed detachment data"));
308 silc_fsm_next(fsm, silc_client_st_resume_error);
309 return SILC_FSM_CONTINUE;
312 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
313 sizeof(client_id))) {
315 SILC_LOG_DEBUG(("Malformed ID"));
316 silc_fsm_next(fsm, silc_client_st_resume_error);
317 return SILC_FSM_CONTINUE;
320 /* Generate authentication data that server will verify */
321 auth = silc_auth_public_key_auth_generate(conn->public_key,
324 conn->internal->hash,
325 &client_id, SILC_ID_CLIENT);
328 silc_fsm_next(fsm, silc_client_st_resume_error);
329 return SILC_FSM_CONTINUE;
332 /* Send RESUME_CLIENT packet to resume to network */
333 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
334 SILC_STR_UI_SHORT(id_len),
335 SILC_STR_DATA(id, id_len),
336 SILC_STR_DATA(silc_buffer_data(auth),
337 silc_buffer_len(auth)),
339 /** Error sending packet */
340 SILC_LOG_DEBUG(("Error sending packet"));
341 silc_fsm_next(fsm, silc_client_st_resume_error);
342 return SILC_FSM_CONTINUE;
345 /** Wait for new ID */
346 conn->internal->registering = TRUE;
347 silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
348 return SILC_FSM_WAIT;
351 /* Resolve the old session information, user mode and joined channels. */
353 SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
355 SilcClientConnection conn = fsm_context;
356 SilcClient client = conn->client;
357 SilcClientResumeSession resume = state_context;
358 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
359 unsigned char **res_argv = NULL;
362 if (conn->internal->disconnected) {
364 silc_fsm_next(fsm, silc_client_st_resume_error);
365 return SILC_FSM_CONTINUE;
368 if (!conn->local_id) {
369 /** Timeout, ID not received */
370 conn->internal->registering = FALSE;
371 silc_fsm_next(fsm, silc_client_st_resume_error);
372 return SILC_FSM_CONTINUE;
375 /** Wait for channels */
376 silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
378 /* Change our nickname */
379 silc_client_change_nickname(client, conn, conn->local_entry,
380 resume->nickname, NULL, NULL, 0);
382 /* Send UMODE command to get our own user mode in the network */
383 SILC_LOG_DEBUG(("Resolving user mode"));
384 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
385 silc_client_command_called_dummy, NULL,
386 1, 1, silc_buffer_data(conn->internal->local_idp),
387 silc_buffer_len(conn->internal->local_idp));
389 if (!resume->channel_count)
390 return SILC_FSM_YIELD;
392 /* Send IDENTIFY command for all channels we know about. These are the
393 channels we've joined to according our detachment data. */
394 for (i = 0; i < resume->channel_count; i++) {
395 SilcChannelEntry channel;
399 SilcChannelID channel_id;
402 if (silc_buffer_unformat(&resume->detach,
404 SILC_STR_UI16_NSTRING(&name, NULL),
405 SILC_STR_UI16_NSTRING(&chid, &chid_len),
406 SILC_STR_UI_INT(NULL),
410 if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id,
413 idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
417 /* Add the channel to cache */
418 channel = silc_client_get_channel_by_id(client, conn, &channel_id);
420 silc_client_add_channel(client, conn, name, 0, &channel_id);
422 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
423 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
425 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
427 res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
428 res_argv_types[res_argc] = res_argc + 5;
430 silc_buffer_free(idp);
433 /* Send IDENTIFY command */
434 SILC_LOG_DEBUG(("Resolving joined channels"));
435 silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
436 silc_client_resume_continue, conn,
437 res_argc, res_argv, res_argv_lens,
440 for (i = 0; i < resume->channel_count; i++)
441 silc_free(res_argv[i]);
443 silc_free(res_argv_lens);
444 silc_free(res_argv_types);
446 return SILC_FSM_WAIT;
449 /* Resolve joined channel modes, users and topics. */
451 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
453 SilcClientConnection conn = fsm_context;
454 SilcClient client = conn->client;
455 SilcClientResumeSession resume = state_context;
456 SilcIDCacheEntry entry;
457 SilcChannelEntry channel;
461 if (conn->internal->disconnected) {
463 silc_fsm_next(fsm, silc_client_st_resume_error);
464 return SILC_FSM_CONTINUE;
467 SILC_LOG_DEBUG(("Resolving channel details"));
469 /** Wait for channel modes */
470 silc_fsm_next(fsm, silc_client_st_resume_completed);
472 if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
473 return SILC_FSM_YIELD;
475 /* Resolve channels' mode, users and topic */
476 resume->channel_count = silc_list_count(channels) * 3;
477 silc_list_start(channels);
478 while ((entry = silc_list_get(channels))) {
479 channel = entry->context;
480 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
484 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
485 silc_client_resume_continue, conn, 1,
486 1, silc_buffer_data(idp),
487 silc_buffer_len(idp));
488 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
489 silc_client_resume_continue, conn, 1,
490 1, silc_buffer_data(idp),
491 silc_buffer_len(idp));
492 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
493 silc_client_resume_continue, conn, 1,
494 1, silc_buffer_data(idp),
495 silc_buffer_len(idp));
496 silc_buffer_free(idp);
499 return SILC_FSM_WAIT;
502 /* Resuming completed */
504 SILC_FSM_STATE(silc_client_st_resume_completed)
506 SilcClientConnection conn = fsm_context;
507 SilcClient client = conn->client;
508 SilcClientResumeSession resume = state_context;
509 SilcIDCacheEntry entry;
510 SilcChannelEntry channel;
513 if (conn->internal->disconnected) {
515 silc_fsm_next(fsm, silc_client_st_resume_error);
516 return SILC_FSM_CONTINUE;
519 if (resume->channel_count > 0) {
520 resume->channel_count--;
521 if (resume->channel_count)
522 return SILC_FSM_WAIT;
525 SILC_LOG_DEBUG(("Resuming completed"));
527 /* Issue IDENTIFY command for itself to get resolved hostname
528 correctly from server. */
529 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
530 silc_client_command_called_dummy, NULL,
531 1, 5, silc_buffer_data(conn->internal->local_idp),
532 silc_buffer_len(conn->internal->local_idp));
534 /* Issue INFO command to fetch the real server name and server
535 information and other stuff. */
536 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
537 silc_client_command_called_dummy, NULL,
538 1, 2, silc_buffer_data(conn->internal->remote_idp),
539 silc_buffer_len(conn->internal->remote_idp));
541 /* Call connection callback. We have now resumed to SILC network. */
542 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL,
543 conn->callback_context);
545 /* Call UMODE command reply. */
546 if (conn->local_entry->mode)
547 silc_client_resume_command_callback(client, conn, SILC_COMMAND_UMODE,
548 conn->local_entry->mode);
550 /* Call NICK command reply. */
551 silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK,
553 conn->local_entry->nickname,
554 &conn->local_entry->id);
556 /* Call JOIN command replies for all joined channel */
557 silc_idcache_get_all(conn->internal->channel_cache, &channels);
558 silc_list_start(channels);
559 while ((entry = silc_list_get(channels))) {
560 SilcHashTableList htl;
561 const char *cipher, *hmac;
563 channel = entry->context;
564 cipher = (channel->internal.send_key ?
565 silc_cipher_get_name(channel->internal.send_key) : NULL);
566 hmac = (channel->internal.hmac ?
567 silc_hmac_get_name(channel->internal.hmac) : NULL);
568 silc_hash_table_list(channel->user_list, &htl);
569 silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
570 channel->channel_name, channel,
571 channel->mode, &htl, channel->topic,
572 cipher, hmac, channel->founder_key,
573 channel->channel_pubkeys,
574 channel->user_limit);
575 silc_hash_table_list_reset(&htl);
578 conn->internal->registering = FALSE;
579 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
580 silc_client_connect_timeout, conn);
581 silc_free(resume->nickname);
583 silc_async_free(conn->internal->cop);
584 conn->internal->cop = NULL;
586 return SILC_FSM_FINISH;
589 /* Error resuming to network */
591 SILC_FSM_STATE(silc_client_st_resume_error)
593 SilcClientConnection conn = fsm_context;
594 SilcClientResumeSession resume = state_context;
596 if (conn->internal->disconnected) {
598 silc_free(resume->nickname);
601 return SILC_FSM_FINISH;
604 SILC_LOG_DEBUG(("Error resuming to network"));
606 /* Signal to close connection */
607 conn->internal->status = SILC_CLIENT_CONN_ERROR;
608 if (!conn->internal->disconnected) {
609 conn->internal->disconnected = TRUE;
610 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
613 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
614 silc_client_connect_timeout, conn);
617 silc_free(resume->nickname);
621 return SILC_FSM_FINISH;
624 /* Generates the session detachment data. This data can be used later
625 to resume back to the server. */
627 SilcBuffer silc_client_get_detach_data(SilcClient client,
628 SilcClientConnection conn)
631 SilcHashTableList htl;
633 unsigned char id[64];
637 SILC_LOG_DEBUG(("Creating detachment data"));
639 ch_count = silc_hash_table_count(conn->local_entry->channels);
640 silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len);
642 /* Save the nickname, Client ID and user mode in SILC network */
643 detach = silc_buffer_alloc(0);
647 silc_buffer_format(detach,
649 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
650 SILC_STR_DATA(conn->local_entry->nickname,
651 strlen(conn->local_entry->nickname)),
652 SILC_STR_UI_SHORT(id_len),
653 SILC_STR_DATA(id, id_len),
654 SILC_STR_UI_INT(conn->local_entry->mode),
655 SILC_STR_UI_INT(ch_count),
658 silc_buffer_free(detach);
662 /* Save all joined channels */
663 silc_hash_table_list(conn->local_entry->channels, &htl);
664 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
665 unsigned char chid[32];
668 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
670 silc_buffer_format(detach,
672 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
673 SILC_STR_DATA(chu->channel->channel_name,
674 strlen(chu->channel->channel_name)),
675 SILC_STR_UI_SHORT(chid_len),
676 SILC_STR_DATA(chid, chid_len),
677 SILC_STR_UI_INT(chu->channel->mode),
680 silc_hash_table_list_reset(&htl);
682 silc_buffer_start(detach);
683 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
684 silc_buffer_len(detach));