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;
87 SILC_LOG_DEBUG(("New ID received from server"));
89 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
90 silc_buffer_len(&packet->buffer), &id))
93 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
96 /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
97 if (conn->internal->remote_version >= 13)
98 nick = (conn->internal->params.nickname ?
99 conn->internal->params.nickname : client->username);
101 nick = client->username;
103 /* Create local client entry */
104 conn->local_entry = silc_client_add_client(client, conn, nick,
108 if (!conn->local_entry)
111 /* Save the ID. Take reference to conn->local_id. */
112 conn->local_id = &conn->local_entry->id;
113 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
116 if (packet->src_id_len) {
117 conn->internal->remote_idp =
118 silc_id_payload_encode_data(packet->src_id,
120 packet->src_id_type);
121 if (!conn->internal->remote_idp)
123 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
124 silc_buffer_len(conn->internal->remote_idp),
128 /* Set IDs to the packet stream */
129 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
130 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
132 /* Signal connection that new ID was received so it can continue
133 with the registering. */
134 if (conn->internal->registering)
135 silc_fsm_continue_sync(&conn->internal->event_thread);
138 /** Packet processed */
139 silc_packet_free(packet);
140 return SILC_FSM_FINISH;
144 /************************ Register to SILC network **************************/
146 /* Register to network */
148 SILC_FSM_STATE(silc_client_st_register)
150 SilcClientConnection conn = fsm_context;
151 SilcClient client = conn->client;
154 SILC_LOG_DEBUG(("Register to network"));
156 /* From SILC protocol version 1.3, nickname is in NEW_CLIENT packet */
157 if (conn->internal->remote_version >= 13)
158 nick = (conn->internal->params.nickname ?
159 conn->internal->params.nickname : client->username);
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)),
169 SILC_STR_UI_SHORT(nick ? strlen(nick) : 0),
170 SILC_STR_DATA(nick, nick ? strlen(nick) : 0),
172 /** Error sending packet */
173 silc_fsm_next(fsm, silc_client_st_register_error);
174 return SILC_FSM_CONTINUE;
177 /** Wait for new ID */
178 conn->internal->registering = TRUE;
179 silc_fsm_next_later(fsm, silc_client_st_register_complete,
180 conn->internal->retry_timer, 0);
181 return SILC_FSM_WAIT;
184 /* Wait for NEW_ID packet to arrive */
186 SILC_FSM_STATE(silc_client_st_register_complete)
188 SilcClientConnection conn = fsm_context;
189 SilcClient client = conn->client;
191 if (conn->internal->disconnected) {
193 silc_fsm_next(fsm, silc_client_st_register_error);
194 return SILC_FSM_CONTINUE;
197 if (!conn->local_id) {
198 if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
199 /** Timeout, ID not received */
200 conn->internal->registering = FALSE;
201 conn->internal->retry_count = 0;
202 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
203 silc_fsm_next(fsm, silc_client_st_register_error);
204 return SILC_FSM_CONTINUE;
207 /** Resend registering packet */
208 silc_fsm_next(fsm, silc_client_st_register);
209 conn->internal->retry_timer = ((conn->internal->retry_timer *
210 SILC_CLIENT_RETRY_MUL) +
211 (silc_rng_get_rn16(client->rng) %
212 SILC_CLIENT_RETRY_RAND));
213 return SILC_FSM_CONTINUE;
216 SILC_LOG_DEBUG(("Registered to network"));
218 /* Issue IDENTIFY command for itself to get resolved hostname
219 correctly from server. */
220 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
221 silc_client_command_called_dummy, NULL,
222 1, 5, silc_buffer_data(conn->internal->local_idp),
223 silc_buffer_len(conn->internal->local_idp));
225 /* With SILC protocol version 1.2 call NICK command if the nickname was
226 set by the application. */
227 if (conn->internal->params.nickname && conn->internal->remote_version < 13 &&
228 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
229 silc_client_command_call(client, conn, NULL,
230 "NICK", conn->internal->params.nickname, NULL);
232 /* Issue INFO command to fetch the real server name and server
233 information and other stuff. */
234 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
235 silc_client_command_called_dummy, NULL,
236 1, 2, silc_buffer_data(conn->internal->remote_idp),
237 silc_buffer_len(conn->internal->remote_idp));
239 /* Call connection callback. We are now inside SILC network. */
240 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
241 conn->callback_context);
243 conn->internal->registering = FALSE;
244 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
245 silc_client_connect_timeout, conn);
246 silc_async_free(conn->internal->cop);
247 conn->internal->cop = NULL;
249 return SILC_FSM_FINISH;
252 /* Error registering to network */
254 SILC_FSM_STATE(silc_client_st_register_error)
256 SilcClientConnection conn = fsm_context;
258 SILC_LOG_DEBUG(("Error registering to network"));
260 /* Signal to close connection */
261 conn->internal->status = SILC_CLIENT_CONN_ERROR;
262 if (!conn->internal->disconnected) {
263 conn->internal->disconnected = TRUE;
264 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
267 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
268 silc_client_connect_timeout, conn);
270 return SILC_FSM_FINISH;
273 /************************* Resume detached session **************************/
275 /* Resume detached session */
277 SILC_FSM_STATE(silc_client_st_resume)
279 SilcClientConnection conn = fsm_context;
280 SilcClient client = conn->client;
281 SilcClientResumeSession resume;
285 SilcClientID client_id;
288 SILC_LOG_DEBUG(("Resuming detached session"));
290 resume = silc_calloc(1, sizeof(*resume));
293 silc_fsm_next(fsm, silc_client_st_resume_error);
294 return SILC_FSM_CONTINUE;
296 silc_fsm_set_state_context(fsm, resume);
298 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
299 conn->internal->params.detach_data_len);
300 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
301 silc_buffer_len(&resume->detach));
303 /* Take the old client ID from the detachment data */
304 ret = silc_buffer_unformat(&resume->detach,
306 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
308 SILC_STR_UI16_NSTRING(&id, &id_len),
309 SILC_STR_UI_INT(NULL),
310 SILC_STR_UI_INT(&resume->channel_count),
313 /** Malformed detach data */
314 SILC_LOG_DEBUG(("Malformed detachment data"));
315 silc_fsm_next(fsm, silc_client_st_resume_error);
316 return SILC_FSM_CONTINUE;
319 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
320 sizeof(client_id))) {
322 SILC_LOG_DEBUG(("Malformed ID"));
323 silc_fsm_next(fsm, silc_client_st_resume_error);
324 return SILC_FSM_CONTINUE;
327 /* Generate authentication data that server will verify */
328 auth = silc_auth_public_key_auth_generate(conn->public_key,
331 conn->internal->hash,
332 &client_id, SILC_ID_CLIENT);
335 silc_fsm_next(fsm, silc_client_st_resume_error);
336 return SILC_FSM_CONTINUE;
339 /* Send RESUME_CLIENT packet to resume to network */
340 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
341 SILC_STR_UI_SHORT(id_len),
342 SILC_STR_DATA(id, id_len),
343 SILC_STR_DATA(silc_buffer_data(auth),
344 silc_buffer_len(auth)),
346 /** Error sending packet */
347 SILC_LOG_DEBUG(("Error sending packet"));
348 silc_fsm_next(fsm, silc_client_st_resume_error);
349 return SILC_FSM_CONTINUE;
352 /** Wait for new ID */
353 conn->internal->registering = TRUE;
354 silc_fsm_next_later(fsm, silc_client_st_resume_resolve_channels, 15, 0);
355 return SILC_FSM_WAIT;
358 /* Resolve the old session information, user mode and joined channels. */
360 SILC_FSM_STATE(silc_client_st_resume_resolve_channels)
362 SilcClientConnection conn = fsm_context;
363 SilcClient client = conn->client;
364 SilcClientResumeSession resume = state_context;
365 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
366 unsigned char **res_argv = NULL;
369 if (conn->internal->disconnected) {
371 silc_fsm_next(fsm, silc_client_st_resume_error);
372 return SILC_FSM_CONTINUE;
375 if (!conn->local_id) {
376 /** Timeout, ID not received */
377 conn->internal->registering = FALSE;
378 silc_fsm_next(fsm, silc_client_st_resume_error);
379 return SILC_FSM_CONTINUE;
382 /** Wait for channels */
383 silc_fsm_next(fsm, silc_client_st_resume_resolve_cmodes);
385 /* Change our nickname */
386 silc_client_change_nickname(client, conn, conn->local_entry,
387 resume->nickname, NULL, NULL, 0);
389 /* Send UMODE command to get our own user mode in the network */
390 SILC_LOG_DEBUG(("Resolving user mode"));
391 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
392 silc_client_command_called_dummy, NULL,
393 1, 1, silc_buffer_data(conn->internal->local_idp),
394 silc_buffer_len(conn->internal->local_idp));
396 if (!resume->channel_count)
397 return SILC_FSM_YIELD;
399 /* Send IDENTIFY command for all channels we know about. These are the
400 channels we've joined to according our detachment data. */
401 for (i = 0; i < resume->channel_count; i++) {
402 SilcChannelEntry channel;
406 SilcChannelID channel_id;
409 if (silc_buffer_unformat(&resume->detach,
411 SILC_STR_UI16_NSTRING(&name, NULL),
412 SILC_STR_UI16_NSTRING(&chid, &chid_len),
413 SILC_STR_UI_INT(NULL),
417 if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id,
420 idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
424 /* Add the channel to cache */
425 channel = silc_client_get_channel_by_id(client, conn, &channel_id);
427 silc_client_add_channel(client, conn, name, 0, &channel_id);
429 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
430 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
432 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
434 res_argv[res_argc] = silc_buffer_steal(idp, &res_argv_lens[res_argc]);
435 res_argv_types[res_argc] = res_argc + 5;
437 silc_buffer_free(idp);
440 /* Send IDENTIFY command */
441 SILC_LOG_DEBUG(("Resolving joined channels"));
442 silc_client_command_send_argv(client, conn, SILC_COMMAND_IDENTIFY,
443 silc_client_resume_continue, conn,
444 res_argc, res_argv, res_argv_lens,
447 for (i = 0; i < resume->channel_count; i++)
448 silc_free(res_argv[i]);
450 silc_free(res_argv_lens);
451 silc_free(res_argv_types);
453 return SILC_FSM_WAIT;
456 /* Resolve joined channel modes, users and topics. */
458 SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
460 SilcClientConnection conn = fsm_context;
461 SilcClient client = conn->client;
462 SilcClientResumeSession resume = state_context;
463 SilcIDCacheEntry entry;
464 SilcChannelEntry channel;
468 if (conn->internal->disconnected) {
470 silc_fsm_next(fsm, silc_client_st_resume_error);
471 return SILC_FSM_CONTINUE;
474 SILC_LOG_DEBUG(("Resolving channel details"));
476 /** Wait for channel modes */
477 silc_fsm_next(fsm, silc_client_st_resume_completed);
479 if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
480 return SILC_FSM_YIELD;
482 /* Resolve channels' mode, users and topic */
483 resume->channel_count = silc_list_count(channels) * 3;
484 silc_list_start(channels);
485 while ((entry = silc_list_get(channels))) {
486 channel = entry->context;
487 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
491 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
492 silc_client_resume_continue, conn, 1,
493 1, silc_buffer_data(idp),
494 silc_buffer_len(idp));
495 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
496 silc_client_resume_continue, conn, 1,
497 1, silc_buffer_data(idp),
498 silc_buffer_len(idp));
499 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
500 silc_client_resume_continue, conn, 1,
501 1, silc_buffer_data(idp),
502 silc_buffer_len(idp));
503 silc_buffer_free(idp);
506 return SILC_FSM_WAIT;
509 /* Resuming completed */
511 SILC_FSM_STATE(silc_client_st_resume_completed)
513 SilcClientConnection conn = fsm_context;
514 SilcClient client = conn->client;
515 SilcClientResumeSession resume = state_context;
516 SilcIDCacheEntry entry;
517 SilcChannelEntry channel;
520 if (conn->internal->disconnected) {
522 silc_fsm_next(fsm, silc_client_st_resume_error);
523 return SILC_FSM_CONTINUE;
526 if (resume->channel_count > 0) {
527 resume->channel_count--;
528 if (resume->channel_count)
529 return SILC_FSM_WAIT;
532 SILC_LOG_DEBUG(("Resuming completed"));
534 /* Issue IDENTIFY command for itself to get resolved hostname
535 correctly from server. */
536 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
537 silc_client_command_called_dummy, NULL,
538 1, 5, silc_buffer_data(conn->internal->local_idp),
539 silc_buffer_len(conn->internal->local_idp));
541 /* Issue INFO command to fetch the real server name and server
542 information and other stuff. */
543 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
544 silc_client_command_called_dummy, NULL,
545 1, 2, silc_buffer_data(conn->internal->remote_idp),
546 silc_buffer_len(conn->internal->remote_idp));
548 /* Call connection callback. We have now resumed to SILC network. */
549 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL,
550 conn->callback_context);
552 /* Call UMODE command reply. */
553 if (conn->local_entry->mode)
554 silc_client_resume_command_callback(client, conn, SILC_COMMAND_UMODE,
555 conn->local_entry->mode);
557 /* Call NICK command reply. */
558 silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK,
560 conn->local_entry->nickname,
561 &conn->local_entry->id);
563 /* Call JOIN command replies for all joined channel */
564 silc_idcache_get_all(conn->internal->channel_cache, &channels);
565 silc_list_start(channels);
566 while ((entry = silc_list_get(channels))) {
567 SilcHashTableList htl;
568 const char *cipher, *hmac;
570 channel = entry->context;
571 cipher = (channel->internal.send_key ?
572 silc_cipher_get_name(channel->internal.send_key) : NULL);
573 hmac = (channel->internal.hmac ?
574 silc_hmac_get_name(channel->internal.hmac) : NULL);
575 silc_hash_table_list(channel->user_list, &htl);
576 silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
577 channel->channel_name, channel,
578 channel->mode, &htl, channel->topic,
579 cipher, hmac, channel->founder_key,
580 channel->channel_pubkeys,
581 channel->user_limit);
582 silc_hash_table_list_reset(&htl);
585 conn->internal->registering = FALSE;
586 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
587 silc_client_connect_timeout, conn);
588 silc_free(resume->nickname);
590 silc_async_free(conn->internal->cop);
591 conn->internal->cop = NULL;
593 return SILC_FSM_FINISH;
596 /* Error resuming to network */
598 SILC_FSM_STATE(silc_client_st_resume_error)
600 SilcClientConnection conn = fsm_context;
601 SilcClientResumeSession resume = state_context;
603 if (conn->internal->disconnected) {
605 silc_free(resume->nickname);
608 return SILC_FSM_FINISH;
611 SILC_LOG_DEBUG(("Error resuming to network"));
613 /* Signal to close connection */
614 conn->internal->status = SILC_CLIENT_CONN_ERROR;
615 if (!conn->internal->disconnected) {
616 conn->internal->disconnected = TRUE;
617 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
620 silc_schedule_task_del_by_all(conn->internal->schedule, 0,
621 silc_client_connect_timeout, conn);
624 silc_free(resume->nickname);
628 return SILC_FSM_FINISH;
631 /* Generates the session detachment data. This data can be used later
632 to resume back to the server. */
634 SilcBuffer silc_client_get_detach_data(SilcClient client,
635 SilcClientConnection conn)
638 SilcHashTableList htl;
640 unsigned char id[64];
644 SILC_LOG_DEBUG(("Creating detachment data"));
646 ch_count = silc_hash_table_count(conn->local_entry->channels);
647 silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len);
649 /* Save the nickname, Client ID and user mode in SILC network */
650 detach = silc_buffer_alloc(0);
654 silc_buffer_format(detach,
656 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
657 SILC_STR_DATA(conn->local_entry->nickname,
658 strlen(conn->local_entry->nickname)),
659 SILC_STR_UI_SHORT(id_len),
660 SILC_STR_DATA(id, id_len),
661 SILC_STR_UI_INT(conn->local_entry->mode),
662 SILC_STR_UI_INT(ch_count),
665 silc_buffer_free(detach);
669 /* Save all joined channels */
670 silc_hash_table_list(conn->local_entry->channels, &htl);
671 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
672 unsigned char chid[32];
675 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
677 silc_buffer_format(detach,
679 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
680 SILC_STR_DATA(chu->channel->channel_name,
681 strlen(chu->channel->channel_name)),
682 SILC_STR_UI_SHORT(chid_len),
683 SILC_STR_DATA(chid, chid_len),
684 SILC_STR_UI_INT(chu->channel->mode),
687 silc_hash_table_list_reset(&htl);
689 silc_buffer_start(detach);
690 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
691 silc_buffer_len(detach));