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;
40 /************************ Static utility functions **************************/
43 /****************************** NEW_ID packet *******************************/
45 /* Received new ID packet from server during registering to SILC network */
47 SILC_FSM_STATE(silc_client_new_id)
49 SilcClientConnection conn = fsm_context;
50 SilcClient client = conn->client;
51 SilcPacket packet = state_context;
57 SILC_LOG_DEBUG(("New ID received from server"));
59 if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer),
60 silc_buffer_len(&packet->buffer), &id))
63 SILC_LOG_DEBUG(("New ID %s", silc_id_render(&id.u.client_id,
66 /* Create local client entry */
67 conn->local_entry = silc_client_add_client(client, conn,
72 if (!conn->local_entry)
76 conn->local_id = &conn->local_entry->id;
77 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
79 /* Save cache entry */
80 if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
82 &conn->internal->local_entry))
86 if (packet->src_id_len) {
87 conn->internal->remote_idp =
88 silc_id_payload_encode_data(packet->src_id,
91 if (!conn->internal->remote_idp)
93 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
94 silc_buffer_len(conn->internal->remote_idp),
98 /* Set IDs to the packet stream */
99 silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
100 conn->remote_id.type, SILC_ID_GET_ID(conn->remote_id));
102 /* Signal connection that new ID was received so it can continue
103 with the registering. */
104 if (conn->internal->registering)
105 silc_fsm_continue_sync(&conn->internal->event_thread);
108 /** Packet processed */
109 silc_packet_free(packet);
110 return SILC_FSM_FINISH;
114 /************************ Register to SILC network **************************/
116 /* Register to network */
118 SILC_FSM_STATE(silc_client_st_register)
120 SilcClientConnection conn = fsm_context;
121 SilcClient client = conn->client;
123 SILC_LOG_DEBUG(("Register to network"));
125 /* Send NEW_CLIENT packet to register to network */
126 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
127 SILC_STR_UI_SHORT(strlen(client->username)),
128 SILC_STR_DATA(client->username,
129 strlen(client->username)),
130 SILC_STR_UI_SHORT(strlen(client->realname)),
131 SILC_STR_DATA(client->realname,
132 strlen(client->realname)),
134 /** Error sending packet */
135 silc_fsm_next(fsm, silc_client_st_register_error);
136 return SILC_FSM_CONTINUE;
139 /** Wait for new ID */
140 conn->internal->registering = TRUE;
141 silc_fsm_next_later(fsm, silc_client_st_register_complete, 15, 0);
142 return SILC_FSM_WAIT;
145 /* Wait for NEW_ID packet to arrive */
147 SILC_FSM_STATE(silc_client_st_register_complete)
149 SilcClientConnection conn = fsm_context;
150 SilcClient client = conn->client;
152 if (!conn->local_id) {
153 /** Timeout, ID not received */
154 conn->internal->registering = FALSE;
155 silc_fsm_next(fsm, silc_client_st_register_error);
156 return SILC_FSM_CONTINUE;
159 SILC_LOG_DEBUG(("Registered to network"));
161 /* Issue IDENTIFY command for itself to get resolved hostname
162 correctly from server. */
163 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, NULL, NULL,
164 1, 5, silc_buffer_data(conn->internal->local_idp),
165 silc_buffer_len(conn->internal->local_idp));
167 /* Send NICK command if the nickname was set by the application (and is
168 not same as the username). Send this with little timeout. */
169 if (conn->internal->params.nickname &&
170 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
171 silc_client_command_send(client, conn, SILC_COMMAND_NICK, NULL, NULL,
172 1, 1, conn->internal->params.nickname,
173 strlen(conn->internal->params.nickname));
175 /* Issue INFO command to fetch the real server name and server
176 information and other stuff. */
177 silc_client_command_send(client, conn, SILC_COMMAND_INFO, NULL, NULL,
178 1, 2, silc_buffer_data(conn->internal->remote_idp),
179 silc_buffer_len(conn->internal->remote_idp));
181 /* Call connection callback. We are now inside SILC network. */
182 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
183 conn->callback_context);
185 conn->internal->registering = FALSE;
186 return SILC_FSM_FINISH;
189 SILC_FSM_STATE(silc_client_st_register_error)
191 SilcClientConnection conn = fsm_context;
192 SilcClient client = conn->client;
195 /* Close connection */
197 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
198 conn->callback_context);
200 return SILC_FSM_FINISH;
204 /************************* Resume detached session **************************/
206 /* Resume detached session */
208 SILC_FSM_STATE(silc_client_st_resume)
210 SilcClientConnection conn = fsm_context;
211 SilcClient client = conn->client;
212 SilcClientResumeSession resume;
218 SILC_LOG_DEBUG(("Resuming detached session"));
220 resume = silc_calloc(1, sizeof(*resume));
223 silc_fsm_next(fsm, silc_client_st_resume_error);
224 return SILC_FSM_CONTINUE;
226 silc_fsm_set_state_context(fsm, resume);
228 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
229 conn->internal->params.detach_data_len);
230 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
231 silc_buffer_len(&resume->detach));
233 /* Take the old client ID from the detachment data */
234 ret = silc_buffer_unformat(&resume->detach,
236 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
238 SILC_STR_UI16_NSTRING(&id, &id_len),
239 SILC_STR_UI_INT(NULL),
240 SILC_STR_UI_INT(&resume->channel_count),
243 /** Malformed detach data */
244 silc_fsm_next(fsm, silc_client_st_resume_error);
245 return SILC_FSM_CONTINUE;
248 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id,
249 sizeof(resume->client_id))) {
251 silc_fsm_next(fsm, silc_client_st_resume_error);
252 return SILC_FSM_CONTINUE;
255 /* Generate authentication data that server will verify */
256 auth = silc_auth_public_key_auth_generate(conn->public_key,
259 conn->internal->hash,
264 silc_fsm_next(fsm, silc_client_st_resume_error);
265 return SILC_FSM_CONTINUE;
268 /* Send RESUME_CLIENT packet to resume to network */
269 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
270 SILC_STR_UI_SHORT(id_len),
271 SILC_STR_UI_XNSTRING(id, id_len),
272 SILC_STR_UI_XNSTRING(silc_buffer_data(auth),
273 silc_buffer_len(auth)),
275 /** Error sending packet */
276 silc_fsm_next(fsm, silc_client_st_resume_error);
277 return SILC_FSM_CONTINUE;
280 /** Wait for new ID */
281 conn->internal->registering = TRUE;
282 silc_fsm_next_later(fsm, silc_client_st_resume_resolve, 15, 0);
283 return SILC_FSM_WAIT;
286 /* Resolve the old session information */
288 SILC_FSM_STATE(silc_client_st_resume_resolve)
291 SilcClientConnection conn = fsm_context;
292 SilcClientResumeSession resume = state_context;
294 if (!conn->local_id) {
295 /** Timeout, ID not received */
296 conn->internal->registering = FALSE;
297 silc_fsm_next(fsm, silc_client_st_resume_error);
298 return SILC_FSM_CONTINUE;
302 for (i = 0; i < ch_count; i++) {
307 SilcChannelID *channel_id;
308 SilcChannelEntry channel_entry;
310 len = silc_buffer_unformat(&detach,
311 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
312 SILC_STR_UI16_NSTRING(&chid, &chid_len),
313 SILC_STR_UI_INT(&ch_mode),
318 /* Add new channel */
319 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
320 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
321 if (!channel_entry) {
322 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
326 silc_free(channel_id);
329 silc_buffer_pull(&detach, len);
333 return SILC_FSM_FINISH;
336 SILC_FSM_STATE(silc_client_st_resume_error)
339 /* Close connection */
341 return SILC_FSM_FINISH;
344 /* Generates the session detachment data. This data can be used later
345 to resume back to the server. */
347 SilcBuffer silc_client_get_detach_data(SilcClient client,
348 SilcClientConnection conn)
351 SilcHashTableList htl;
355 SILC_LOG_DEBUG(("Creating detachment data"));
357 ch_count = silc_hash_table_count(conn->local_entry->channels);
359 /* Save the nickname, Client ID and user mode in SILC network */
360 detach = silc_buffer_alloc(0);
364 silc_buffer_format(detach,
366 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
367 SILC_STR_DATA(conn->local_entry->nickname,
368 strlen(conn->local_entry->nickname)),
369 SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
371 SILC_STR_DATA(silc_buffer_data(conn->internal->
373 silc_buffer_len(conn->internal->
375 SILC_STR_UI_INT(conn->local_entry->mode),
376 SILC_STR_UI_INT(ch_count),
379 silc_buffer_free(detach);
383 /* Save all joined channels */
384 silc_hash_table_list(conn->local_entry->channels, &htl);
385 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
386 unsigned char chid[32];
389 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
391 silc_buffer_format(detach,
393 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
394 SILC_STR_DATA(chu->channel->channel_name,
395 strlen(chu->channel->channel_name)),
396 SILC_STR_UI_SHORT(chid_len),
397 SILC_STR_DATA(chid, chid_len),
398 SILC_STR_UI_INT(chu->channel->mode),
402 silc_hash_table_list_reset(&htl);
404 silc_buffer_start(detach);
405 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
406 silc_buffer_len(detach));