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 /* Create local client entry */
64 conn->local_entry = silc_client_add_client(client, conn,
69 if (!conn->local_entry)
73 conn->local_id = &conn->local_entry->id;
74 conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
76 /* Save cache entry */
77 silc_idcache_find_by_id_one(conn->internal->client_cache, conn->local_id,
78 &conn->internal->local_entry);
81 if (packet->src_id_len) {
82 conn->internal->remote_idp =
83 silc_id_payload_encode_data(packet->src_id,
86 if (!conn->internal->remote_idp)
88 silc_id_payload_parse_id(silc_buffer_data(conn->internal->remote_idp),
89 silc_buffer_len(conn->internal->remote_idp),
93 /* Signal connection that new ID was received so it can continue
94 with the registering. */
95 if (conn->internal->registering)
96 silc_fsm_continue_sync(&conn->internal->event_thread);
99 /** Packet processed */
100 silc_packet_free(packet);
101 return SILC_FSM_FINISH;
105 /************************ Register to SILC network **************************/
107 /* Register to network */
109 SILC_FSM_STATE(silc_client_st_register)
111 SilcClientConnection conn = fsm_context;
112 SilcClient client = conn->client;
114 SILC_LOG_DEBUG(("Register to network"));
116 /* Send NEW_CLIENT packet to register to network */
117 if (!silc_packet_send_va(conn->stream, SILC_PACKET_NEW_CLIENT, 0,
118 SILC_STR_UI_SHORT(strlen(client->username)),
119 SILC_STR_DATA(client->username,
120 strlen(client->username)),
121 SILC_STR_UI_SHORT(strlen(client->realname)),
122 SILC_STR_DATA(client->realname,
123 strlen(client->realname)),
125 /** Error sending packet */
126 silc_fsm_next(fsm, silc_client_st_register_error);
127 return SILC_FSM_CONTINUE;
130 /** Wait for new ID */
131 conn->internal->registering = TRUE;
132 silc_fsm_next_later(fsm, silc_client_st_register_complete, 15, 0);
133 return SILC_FSM_WAIT;
136 /* Wait for NEW_ID packet to arrive */
138 SILC_FSM_STATE(silc_client_st_register_complete)
140 SilcClientConnection conn = fsm_context;
141 SilcClient client = conn->client;
143 if (!conn->local_id) {
144 /** Timeout, ID not received */
145 conn->internal->registering = FALSE;
146 silc_fsm_next(fsm, silc_client_st_register_error);
147 return SILC_FSM_CONTINUE;
150 SILC_LOG_DEBUG(("Registered to network"));
152 /* Issue IDENTIFY command for itself to get resolved hostname
153 correctly from server. */
154 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, NULL, NULL,
155 1, 5, silc_buffer_data(conn->internal->local_idp),
156 silc_buffer_len(conn->internal->local_idp));
158 /* Send NICK command if the nickname was set by the application (and is
159 not same as the username). Send this with little timeout. */
160 if (conn->internal->params.nickname &&
161 !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username))
162 silc_client_command_send(client, conn, SILC_COMMAND_NICK, NULL, NULL,
163 1, 1, conn->internal->params.nickname,
164 strlen(conn->internal->params.nickname));
166 /* Issue INFO command to fetch the real server name and server
167 information and other stuff. */
168 silc_client_command_send(client, conn, SILC_COMMAND_INFO, NULL, NULL,
169 1, 2, silc_buffer_data(conn->internal->remote_idp),
170 silc_buffer_len(conn->internal->remote_idp));
172 /* Call connection callback. We are now inside SILC network. */
173 conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS, 0, NULL,
174 conn->callback_context);
176 conn->internal->registering = FALSE;
177 return SILC_FSM_FINISH;
180 SILC_FSM_STATE(silc_client_st_register_error)
182 SilcClientConnection conn = fsm_context;
183 SilcClient client = conn->client;
186 /* Close connection */
188 conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
189 conn->callback_context);
191 return SILC_FSM_FINISH;
195 /************************* Resume detached session **************************/
197 /* Resume detached session */
199 SILC_FSM_STATE(silc_client_st_resume)
201 SilcClientConnection conn = fsm_context;
202 SilcClient client = conn->client;
203 SilcClientResumeSession resume;
209 SILC_LOG_DEBUG(("Resuming detached session"));
211 resume = silc_calloc(1, sizeof(*resume));
214 silc_fsm_next(fsm, silc_client_st_resume_error);
215 return SILC_FSM_CONTINUE;
217 silc_fsm_set_state_context(fsm, resume);
219 silc_buffer_set(&resume->detach, conn->internal->params.detach_data,
220 conn->internal->params.detach_data_len);
221 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(&resume->detach),
222 silc_buffer_len(&resume->detach));
224 /* Take the old client ID from the detachment data */
225 ret = silc_buffer_unformat(&resume->detach,
227 SILC_STR_UI16_NSTRING_ALLOC(&resume->nickname,
229 SILC_STR_UI16_NSTRING(&id, &id_len),
230 SILC_STR_UI_INT(NULL),
231 SILC_STR_UI_INT(&resume->channel_count),
234 /** Malformed detach data */
235 silc_fsm_next(fsm, silc_client_st_resume_error);
236 return SILC_FSM_CONTINUE;
239 if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id,
240 sizeof(resume->client_id))) {
242 silc_fsm_next(fsm, silc_client_st_resume_error);
243 return SILC_FSM_CONTINUE;
246 /* Generate authentication data that server will verify */
247 auth = silc_auth_public_key_auth_generate(conn->public_key,
250 conn->internal->hash,
255 silc_fsm_next(fsm, silc_client_st_resume_error);
256 return SILC_FSM_CONTINUE;
259 /* Send RESUME_CLIENT packet to resume to network */
260 if (!silc_packet_send_va(conn->stream, SILC_PACKET_RESUME_CLIENT, 0,
261 SILC_STR_UI_SHORT(id_len),
262 SILC_STR_UI_XNSTRING(id, id_len),
263 SILC_STR_UI_XNSTRING(silc_buffer_data(auth),
264 silc_buffer_len(auth)),
266 /** Error sending packet */
267 silc_fsm_next(fsm, silc_client_st_resume_error);
268 return SILC_FSM_CONTINUE;
271 /** Wait for new ID */
272 conn->internal->registering = TRUE;
273 silc_fsm_next_later(fsm, silc_client_st_resume_resolve, 15, 0);
274 return SILC_FSM_WAIT;
277 /* Resolve the old session information */
279 SILC_FSM_STATE(silc_client_st_resume_resolve)
282 SilcClientConnection conn = fsm_context;
283 SilcClientResumeSession resume = state_context;
285 if (!conn->local_id) {
286 /** Timeout, ID not received */
287 conn->internal->registering = FALSE;
288 silc_fsm_next(fsm, silc_client_st_resume_error);
289 return SILC_FSM_CONTINUE;
293 for (i = 0; i < ch_count; i++) {
298 SilcChannelID *channel_id;
299 SilcChannelEntry channel_entry;
301 len = silc_buffer_unformat(&detach,
302 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
303 SILC_STR_UI16_NSTRING(&chid, &chid_len),
304 SILC_STR_UI_INT(&ch_mode),
309 /* Add new channel */
310 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
311 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
312 if (!channel_entry) {
313 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
317 silc_free(channel_id);
320 silc_buffer_pull(&detach, len);
324 return SILC_FSM_FINISH;
327 SILC_FSM_STATE(silc_client_st_resume_error)
330 /* Close connection */
332 return SILC_FSM_FINISH;
335 /* Generates the session detachment data. This data can be used later
336 to resume back to the server. */
338 SilcBuffer silc_client_get_detach_data(SilcClient client,
339 SilcClientConnection conn)
342 SilcHashTableList htl;
346 SILC_LOG_DEBUG(("Creating detachment data"));
348 ch_count = silc_hash_table_count(conn->local_entry->channels);
350 /* Save the nickname, Client ID and user mode in SILC network */
351 detach = silc_buffer_alloc(0);
355 silc_buffer_format(detach,
357 SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
358 SILC_STR_DATA(conn->local_entry->nickname,
359 strlen(conn->local_entry->nickname)),
360 SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
362 SILC_STR_DATA(silc_buffer_data(conn->internal->
364 silc_buffer_len(conn->internal->
366 SILC_STR_UI_INT(conn->local_entry->mode),
367 SILC_STR_UI_INT(ch_count),
370 silc_buffer_free(detach);
374 /* Save all joined channels */
375 silc_hash_table_list(conn->local_entry->channels, &htl);
376 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
377 unsigned char chid[32];
380 silc_id_id2str(&chu->channel->id, SILC_ID_CHANNEL, chid, sizeof(chid),
382 silc_buffer_format(detach,
384 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
385 SILC_STR_DATA(chu->channel->channel_name,
386 strlen(chu->channel->channel_name)),
387 SILC_STR_UI_SHORT(chid_len),
388 SILC_STR_DATA(chid, chid_len),
389 SILC_STR_UI_INT(chu->channel->mode),
393 silc_hash_table_list_reset(&htl);
395 silc_buffer_start(detach);
396 SILC_LOG_HEXDUMP(("Detach data"), silc_buffer_data(detach),
397 silc_buffer_len(detach));