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 /****************************** NEW_ID packet *******************************/
57 /* Received new ID packet from server during registering to SILC network */
59 SILC_FSM_STATE(silc_client_new_id)
61 SilcClientConnection conn = fsm_context;
62 SilcClient client = conn->client;
63 SilcPacket packet = state_context;
69 SILC_LOG_DEBUG(("New ID received from server"));
71 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
72 silc_buffer_len(&packet->buffer), &id))
75 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
78 /* Create local client entry */
79 conn->local_entry = silc_client_add_client(client, conn,
84 if (!conn->local_entry)
88 conn->local_id = &conn->local_entry->id;
89 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
91 /* Save cache entry */
92 silc_mutex_lock(conn->internal->lock);
93 if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
95 &conn->internal->local_entry)) {
96 silc_mutex_unlock(conn->internal->lock);
99 silc_mutex_unlock(conn->internal->lock);
102 if (packet->src_id_len) {
103 conn->internal->remote_idp =
104 silc_id_payload_encode_data(packet->src_id,
106 packet->src_id_type);
107 if (!conn->internal->remote_idp)
109 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
110 silc_buffer_len(conn->internal->remote_idp),
114 /* Set IDs to the packet stream */
115 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
116 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
118 /* Signal connection that new ID was received so it can continue
119 with the registering. */
120 if (conn->internal->registering)
121 silc_fsm_continue_sync(&conn->internal->event_thread);
124 /** Packet processed */
125 silc_packet_free(packet);
126 return SILC_FSM_FINISH;
130 /************************ Register to SILC network **************************/
132 /* Register to network */
134 SILC_FSM_STATE(silc_client_st_register)
136 SilcClientConnection conn = fsm_context;
137 SilcClient client = conn->client;
139 SILC_LOG_DEBUG(("Register to network"));
141 /* Send NEW_CLIENT packet to register to network */
142 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
143 SILC_STR_UI_SHORT(strlen(client->username)),
144 SILC_STR_DATA(client->username,
145 strlen(client->username)),
146 SILC_STR_UI_SHORT(strlen(client->realname)),
147 SILC_STR_DATA(client->realname,
148 strlen(client->realname)),
150 /** Error sending packet */
151 silc_fsm_next(fsm, silc_client_st_register_error);
152 return SILC_FSM_CONTINUE;
155 /** Wait for new ID */
156 conn->internal->registering = TRUE;
157 silc_fsm_next_later(fsm, silc_client_st_register_complete,
158 conn->internal->retry_timer, 0);
159 return SILC_FSM_WAIT;
162 /* Wait for NEW_ID packet to arrive */
164 SILC_FSM_STATE(silc_client_st_register_complete)
166 SilcClientConnection conn = fsm_context;
167 SilcClient client = conn->client;
169 if (!conn->local_id) {
170 if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) {
171 /** Timeout, ID not received */
172 conn->internal->registering = FALSE;
173 conn->internal->retry_count = 0;
174 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
175 silc_fsm_next(fsm, silc_client_st_register_error);
176 return SILC_FSM_CONTINUE;
179 /** Resend registering packet */
180 silc_fsm_next(fsm, silc_client_st_register);
181 conn->internal->retry_timer = ((conn->internal->retry_timer *
182 SILC_CLIENT_RETRY_MUL) +
183 (silc_rng_get_rn16(client->rng) %
184 SILC_CLIENT_RETRY_RAND));
185 return SILC_FSM_CONTINUE;
188 SILC_LOG_DEBUG(("Registered to network"));
190 /* Issue IDENTIFY command for itself to get resolved hostname
191 correctly from server. */
192 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
193 silc_client_register_command_called, NULL,
194 1, 5, silc_buffer_data(conn->internal->local_idp),
195 silc_buffer_len(conn->internal->local_idp));
197 /* Call NICK command if the nickname was set by the application (and is
198 not same as the username). */
199 if (conn->internal->params.nickname &&
200 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
201 silc_client_command_call(client, conn, NULL,
202 "NICK", conn->internal->params.nickname, NULL);
204 /* Issue INFO command to fetch the real server name and server
205 information and other stuff. */
206 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
207 silc_client_register_command_called, NULL,
208 1, 2, silc_buffer_data(conn->internal->remote_idp),
209 silc_buffer_len(conn->internal->remote_idp));
211 /* Call connection callback. We are now inside SILC network. */
212 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
213 conn->callback_context);
215 conn->internal->registering = FALSE;
216 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
218 return SILC_FSM_FINISH;
221 /* Error registering to network */
223 SILC_FSM_STATE(silc_client_st_register_error)
225 SilcClientConnection conn = fsm_context;
226 SilcClient client = conn->client;
228 SILC_LOG_DEBUG(("Error registering to network"));
230 /* Signal to close connection */
231 if (!conn->internal->disconnected) {
232 conn->internal->disconnected = TRUE;
233 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
236 /* Call connect callback */
237 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
238 conn->callback_context);
240 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
242 return SILC_FSM_FINISH;
246 /************************* Resume detached session **************************/
248 /* Resume detached session */
250 SILC_FSM_STATE(silc_client_st_resume)
252 SilcClientConnection conn = fsm_context;
253 SilcClient client = conn->client;
254 SilcClientResumeSession resume;
260 SILC_LOG_DEBUG(("Resuming detached session"));
262 resume = silc_calloc(1, sizeof(*resume));
265 silc_fsm_next(fsm, silc_client_st_resume_error);
266 return SILC_FSM_CONTINUE;
268 silc_fsm_set_state_context(fsm, resume);
270 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
271 conn->internal->params.detach_data_len);
272 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
273 silc_buffer_len(&resume->detach));
275 /* Take the old client ID from the detachment data */
276 ret = silc_buffer_unformat(&resume->detach,
278 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
280 SILC_STR_UI16_NSTRING(&id, &id_len),
281 SILC_STR_UI_INT(NULL),
282 SILC_STR_UI_INT(&resume->channel_count),
285 /** Malformed detach data */
286 silc_fsm_next(fsm, silc_client_st_resume_error);
287 return SILC_FSM_CONTINUE;
290 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id,
291 sizeof(resume->client_id))) {
293 silc_fsm_next(fsm, silc_client_st_resume_error);
294 return SILC_FSM_CONTINUE;
297 /* Generate authentication data that server will verify */
298 auth = silc_auth_public_key_auth_generate(conn->public_key,
301 conn->internal->hash,
306 silc_fsm_next(fsm, silc_client_st_resume_error);
307 return SILC_FSM_CONTINUE;
310 /* Send RESUME_CLIENT packet to resume to network */
311 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
312 SILC_STR_UI_SHORT(id_len),
313 SILC_STR_UI_XNSTRING(id, id_len),
314 SILC_STR_UI_XNSTRING(silc_buffer_data(auth),
315 silc_buffer_len(auth)),
317 /** Error sending packet */
318 silc_fsm_next(fsm, silc_client_st_resume_error);
319 return SILC_FSM_CONTINUE;
322 /** Wait for new ID */
323 conn->internal->registering = TRUE;
324 silc_fsm_next_later(fsm, silc_client_st_resume_resolve, 15, 0);
325 return SILC_FSM_WAIT;
328 /* Resolve the old session information */
330 SILC_FSM_STATE(silc_client_st_resume_resolve)
333 SilcClientConnection conn = fsm_context;
334 SilcClientResumeSession resume = state_context;
336 if (!conn->local_id) {
337 /** Timeout, ID not received */
338 conn->internal->registering = FALSE;
339 silc_fsm_next(fsm, silc_client_st_resume_error);
340 return SILC_FSM_CONTINUE;
344 for (i = 0; i < ch_count; i++) {
349 SilcChannelID *channel_id;
350 SilcChannelEntry channel_entry;
352 len = silc_buffer_unformat(&detach,
353 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
354 SILC_STR_UI16_NSTRING(&chid, &chid_len),
355 SILC_STR_UI_INT(&ch_mode),
360 /* Add new channel */
361 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
362 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
363 if (!channel_entry) {
364 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
368 silc_free(channel_id);
371 silc_buffer_pull(&detach, len);
375 return SILC_FSM_FINISH;
378 SILC_FSM_STATE(silc_client_st_resume_error)
381 /* Close connection */
383 return SILC_FSM_FINISH;
386 /* Generates the session detachment data. This data can be used later
387 to resume back to the server. */
389 SilcBuffer silc_client_get_detach_data(SilcClient client,
390 SilcClientConnection conn)
393 SilcHashTableList htl;
397 SILC_LOG_DEBUG(("Creating detachment data"));
399 ch_count = silc_hash_table_count(conn->local_entry->channels);
401 /* Save the nickname, Client ID and user mode in SILC network */
402 detach = silc_buffer_alloc(0);
406 silc_buffer_format(detach,
408 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
409 SILC_STR_DATA(conn->local_entry->nickname,
410 strlen(conn->local_entry->nickname)),
411 SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
413 SILC_STR_DATA(silc_buffer_data(conn->internal->
415 silc_buffer_len(conn->internal->
417 SILC_STR_UI_INT(conn->local_entry->mode),
418 SILC_STR_UI_INT(ch_count),
421 silc_buffer_free(detach);
425 /* Save all joined channels */
426 silc_hash_table_list(conn->local_entry->channels, &htl);
427 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
428 unsigned char chid[32];
431 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
433 silc_buffer_format(detach,
435 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
436 SILC_STR_DATA(chu->channel->channel_name,
437 strlen(chu->channel->channel_name)),
438 SILC_STR_UI_SHORT(chid_len),
439 SILC_STR_DATA(chid, chid_len),
440 SILC_STR_UI_INT(chu->channel->mode),
444 silc_hash_table_list_reset(&htl);
446 silc_buffer_start(detach);
447 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
448 silc_buffer_len(detach));