5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002 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 "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
25 SILC_CLIENT_CMD_REPLY_FUNC(resume);
26 SILC_CLIENT_CMD_FUNC(resume_identify);
27 SILC_CLIENT_CMD_FUNC(resume_cmode);
28 SILC_CLIENT_CMD_FUNC(resume_users);
30 #define RESUME_CALL_COMPLETION(client, session, s) \
32 session->success = s; \
33 silc_schedule_task_add(client->schedule, 0, \
34 silc_client_resume_call_completion, session, \
35 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); \
38 /* Generates the session detachment data. This data can be used later
39 to resume back to the server. */
41 SilcBuffer silc_client_get_detach_data(SilcClient client,
42 SilcClientConnection conn)
45 SilcHashTableList htl;
49 SILC_LOG_DEBUG(("Creating detachment data"));
51 ch_count = silc_hash_table_count(conn->local_entry->channels);
53 /* Save the nickname, Client ID and user mode in SILC network */
54 detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) +
55 2 + conn->local_id_data_len + 4 + 4);
56 silc_buffer_format(detach,
57 SILC_STR_UI_SHORT(strlen(conn->nickname)),
58 SILC_STR_UI_XNSTRING(conn->nickname,
59 strlen(conn->nickname)),
60 SILC_STR_UI_SHORT(conn->local_id_data_len),
61 SILC_STR_UI_XNSTRING(conn->local_id_data,
62 conn->local_id_data_len),
63 SILC_STR_UI_INT(conn->local_entry->mode),
64 SILC_STR_UI_INT(ch_count),
67 /* Save all joined channels */
68 silc_hash_table_list(conn->local_entry->channels, &htl);
69 while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
70 unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL);
71 SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL);
73 detach = silc_buffer_realloc(detach, detach->truelen + 2 +
74 strlen(chu->channel->channel_name) +
76 silc_buffer_pull(detach, detach->len);
77 silc_buffer_pull_tail(detach, 2 + strlen(chu->channel->channel_name) +
79 silc_buffer_format(detach,
80 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
81 SILC_STR_UI_XNSTRING(chu->channel->channel_name,
82 strlen(chu->channel->channel_name)),
83 SILC_STR_UI_SHORT(chid_len),
84 SILC_STR_UI_XNSTRING(chid, chid_len),
85 SILC_STR_UI_INT(chu->channel->mode),
89 silc_hash_table_list_reset(&htl);
91 silc_buffer_push(detach, detach->data - detach->head);
93 SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len);
98 /* Processes the detachment data. This creates channels and other
99 stuff according the data found in the the connection parameters.
100 This doesn't actually resolve any detailed information from the
101 server. To do that call silc_client_resume_session function.
102 This returns the old detached session client ID. */
104 bool silc_client_process_detach_data(SilcClient client,
105 SilcClientConnection conn,
106 unsigned char **old_id,
107 SilcUInt16 *old_id_len)
109 SilcBufferStruct detach;
113 SILC_LOG_DEBUG(("Start"));
115 silc_free(conn->nickname);
116 silc_buffer_set(&detach, conn->params.detach_data,
117 conn->params.detach_data_len);
119 SILC_LOG_HEXDUMP(("Detach data"), detach.data, detach.len);
121 /* Take the old client ID from the detachment data */
122 len = silc_buffer_unformat(&detach,
123 SILC_STR_UI16_NSTRING_ALLOC(&conn->nickname,
125 SILC_STR_UI16_NSTRING_ALLOC(old_id, old_id_len),
126 SILC_STR_UI_INT(NULL),
127 SILC_STR_UI_INT(&ch_count),
132 silc_buffer_pull(&detach, len);
134 for (i = 0; i < ch_count; i++) {
139 SilcChannelID *channel_id;
140 SilcChannelEntry channel_entry;
142 len = silc_buffer_unformat(&detach,
143 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
144 SILC_STR_UI16_NSTRING(&chid, &chid_len),
145 SILC_STR_UI_INT(&ch_mode),
150 /* Add new channel */
151 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
152 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
153 if (!channel_entry) {
154 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
158 silc_free(channel_id);
161 silc_buffer_pull(&detach, len);
163 silc_buffer_push(&detach, detach.data - detach.head);
169 /* Resume session context */
172 SilcClientConnection conn;
173 SilcClientResumeSessionCallback callback;
175 SilcUInt32 channel_count;
176 SilcUInt32 *cmd_idents;
177 SilcUInt32 cmd_idents_count;
179 } *SilcClientResumeSession;
181 /* Generic command reply callback. */
183 SILC_CLIENT_CMD_REPLY_FUNC(resume)
185 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
186 SILC_LOG_DEBUG(("Start"));
187 SILC_CLIENT_PENDING_EXEC(cmd, silc_command_get(cmd->payload));
190 /* Special command reply callback for IDENTIFY callbacks. This calls
191 the pending callback for every returned command entry. */
193 SILC_CLIENT_CMD_REPLY_FUNC(resume_special)
195 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
198 SILC_LOG_DEBUG(("Start"));
199 for (i = 0; i < cmd->callbacks_count; i++)
200 if (cmd->callbacks[i].callback)
201 (*cmd->callbacks[i].callback)(cmd->callbacks[i].context, cmd);
204 /* Completion calling callback */
206 SILC_TASK_CALLBACK(silc_client_resume_call_completion)
208 SilcClientResumeSession session = context;
211 session->callback(session->client, session->conn, session->success,
214 for (i = 0; i < session->cmd_idents_count; i++)
215 silc_client_command_pending_del(session->conn, SILC_COMMAND_IDENTIFY,
216 session->cmd_idents[i]);
217 silc_free(session->cmd_idents);
219 memset(session, 'F', sizeof(*session));
223 /* This function is used to perform the resuming procedure after the
224 client has connected to the server properly and has received the
225 Client ID for the resumed session. This resolves all channels
226 that the resumed client is joined, joined users, users modes
227 and channel modes. The `callback' is called after this procedure
230 void silc_client_resume_session(SilcClient client,
231 SilcClientConnection conn,
232 SilcClientResumeSessionCallback callback,
235 SilcClientResumeSession session;
236 SilcIDCacheList list;
237 SilcIDCacheEntry entry;
238 SilcChannelEntry channel;
243 SILC_LOG_DEBUG(("Resuming detached session"));
245 session = silc_calloc(1, sizeof(*session));
247 callback(client, conn, FALSE, context);
250 session->client = client;
251 session->conn = conn;
252 session->callback = callback;
253 session->context = context;
255 /* First, send UMODE commandto get our own user mode in the network */
256 SILC_LOG_DEBUG(("Sending UMODE"));
257 tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
258 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
259 conn->cmd_ident, 1, 1, tmp->data, tmp->len);
260 silc_buffer_free(tmp);
262 /* Second, send IDENTIFY command of all channels we know about. These
263 are the channels we've joined to according our detachment data. */
264 if (silc_idcache_get_all(conn->channel_cache, &list)) {
265 unsigned char **res_argv = NULL;
266 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
268 session->channel_count = silc_idcache_list_count(list);
270 ret = silc_idcache_list_first(list, &entry);
272 channel = entry->context;
273 tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
274 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
275 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
277 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
279 res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
280 res_argv_lens[res_argc] = tmp->len;
281 res_argv_types[res_argc] = res_argc + 5;
283 silc_buffer_free(tmp);
284 ret = silc_idcache_list_next(list, &entry);
286 silc_idcache_list_free(list);
289 /* Send the IDENTIFY command */
290 SILC_LOG_DEBUG(("Sending IDENTIFY"));
291 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
292 silc_client_command_reply_resume_special,
293 0, ++conn->cmd_ident);
294 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
296 silc_client_command_resume_identify,
299 tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
300 res_argc, res_argv, res_argv_lens,
301 res_argv_types, conn->cmd_ident);
302 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
303 NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
305 session->cmd_idents = silc_realloc(session->cmd_idents,
306 sizeof(*session->cmd_idents) *
307 (session->cmd_idents_count + 1));
308 session->cmd_idents[session->cmd_idents_count] = conn->cmd_ident;
309 session->cmd_idents_count++;
311 for (i = 0; i < res_argc; i++)
312 silc_free(res_argv[i]);
314 silc_free(res_argv_lens);
315 silc_free(res_argv_types);
316 silc_buffer_free(tmp);
320 if (!session->channel_count)
321 RESUME_CALL_COMPLETION(client, session, TRUE);
323 /* Now, we wait for replies to come back and then continue with USERS,
324 CMODE and TOPIC commands. */
327 /* Received identify reply for a channel entry */
329 SILC_CLIENT_CMD_FUNC(resume_identify)
331 SilcClientResumeSession session = context;
332 SilcClientCommandReplyContext cmd = context2;
333 SilcClient client = session->client;
334 SilcClientConnection conn = session->conn;
337 SilcChannelEntry channel = NULL;
338 SilcChannelID *channel_id;
342 SILC_LOG_DEBUG(("Start"));
344 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
348 if (cmd->error != SILC_STATUS_OK) {
349 /* Delete unknown channel from our cache */
350 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
351 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
353 channel = silc_client_get_channel_by_id(client, conn, channel_id);
355 silc_client_del_channel(client, conn, channel);
356 silc_free(channel_id);
362 idp = silc_id_payload_parse(tmp, tmp_len);
366 id_type = silc_id_payload_get_type(idp);
369 case SILC_ID_CHANNEL:
370 channel_id = silc_id_payload_get_id(idp);
371 channel = silc_client_get_channel_by_id(client, conn, channel_id);
372 silc_free(channel_id);
375 silc_id_payload_free(idp);
380 /* Now, send CMODE command for this channel. We send only this one
381 because this will return also error if we are not currently joined
382 on this channel, plus we get the channel mode. USERS and TOPIC
383 commands are called after this returns. */
385 SILC_LOG_DEBUG(("Sending CMODE"));
386 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
387 silc_client_command_reply_resume, 0,
389 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
390 conn->cmd_ident, 1, 1, tmp, tmp_len);
391 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
392 silc_client_command_resume_cmode, session);
395 silc_id_payload_free(idp);
397 if (cmd->status != SILC_STATUS_OK &&
398 cmd->status != SILC_STATUS_LIST_END)
401 /* Unregister this command reply */
402 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
403 silc_client_command_reply_resume,
408 session->channel_count--;
409 if (!session->channel_count)
410 RESUME_CALL_COMPLETION(client, session, FALSE);
413 /* Received cmode to channel entry */
415 SILC_CLIENT_CMD_FUNC(resume_cmode)
417 SilcClientResumeSession session = context;
418 SilcClientCommandReplyContext cmd = context2;
419 SilcClient client = session->client;
420 SilcClientConnection conn = session->conn;
422 SilcChannelID *channel_id;
423 SilcChannelEntry channel;
426 SILC_LOG_DEBUG(("Start"));
428 /* Unregister this command reply */
429 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
430 silc_client_command_reply_resume,
433 if (cmd->error != SILC_STATUS_OK)
436 /* Take Channel ID */
437 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
440 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
444 /* Get the channel entry */
445 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
448 /* Get channel mode */
449 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
451 SILC_GET32_MSB(channel->mode, tmp);
453 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
455 /* And now, we will send USERS to get users on the channel */
456 SILC_LOG_DEBUG(("Sending USERS"));
457 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
458 silc_client_command_reply_users_i, 0,
460 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
461 conn->cmd_ident, 1, 1, tmp, len);
462 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
463 silc_client_command_resume_users, session);
466 silc_free(channel_id);
470 session->channel_count--;
471 if (!session->channel_count)
472 RESUME_CALL_COMPLETION(client, session, FALSE);
475 /* Received users reply to a channel entry */
477 SILC_CLIENT_CMD_FUNC(resume_users)
479 SilcClientResumeSession session = context;
480 SilcClientCommandReplyContext cmd = context2;
481 SilcClient client = session->client;
482 SilcClientConnection conn = session->conn;
483 SilcBufferStruct client_id_list, client_mode_list;
485 SilcUInt32 tmp_len, list_count;
486 SilcChannelEntry channel;
487 SilcChannelID *channel_id = NULL;
489 SILC_LOG_DEBUG(("Start"));
491 /* Unregister this command reply */
492 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
493 silc_client_command_reply_users_i,
496 if (cmd->error != SILC_STATUS_OK)
500 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
505 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
511 /* Get the list count */
512 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
517 SILC_GET32_MSB(list_count, tmp);
519 /* Get Client ID list */
520 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
525 silc_buffer_set(&client_id_list, tmp, tmp_len);
527 /* Get client mode list */
528 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
533 silc_buffer_set(&client_mode_list, tmp, tmp_len);
535 /* Get channel entry */
536 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
540 /* Send fake JOIN command reply to application */
541 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
542 SILC_COMMAND_JOIN, cmd->status,
543 channel->channel_name, channel,
545 NULL, NULL, NULL, NULL,
546 channel->hmac, list_count,
547 &client_id_list, client_mode_list);
549 /* Send TOPIC for this channel to get the topic */
550 SILC_LOG_DEBUG(("Sending TOPIC"));
551 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
552 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
553 ++conn->cmd_ident, 1, 1, tmp, tmp_len);
555 /* Call the completion callback after we've got reply to all of
557 session->channel_count--;
558 if (!session->channel_count)
559 RESUME_CALL_COMPLETION(client, session, TRUE);
561 silc_free(channel_id);
565 silc_free(channel_id);
566 session->channel_count--;
567 if (!session->channel_count)
568 RESUME_CALL_COMPLETION(client, session, FALSE);