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 /* Now, we wait for replies to come back and then continue with USERS,
321 CMODE and TOPIC commands. */
324 /* Received identify reply for a channel entry */
326 SILC_CLIENT_CMD_FUNC(resume_identify)
328 SilcClientResumeSession session = context;
329 SilcClientCommandReplyContext cmd = context2;
330 SilcClient client = session->client;
331 SilcClientConnection conn = session->conn;
334 SilcChannelEntry channel = NULL;
335 SilcChannelID *channel_id;
339 SILC_LOG_DEBUG(("Start"));
341 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
345 if (cmd->error != SILC_STATUS_OK) {
346 /* Delete unknown channel from our cache */
347 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
348 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
350 channel = silc_client_get_channel_by_id(client, conn, channel_id);
352 silc_client_del_channel(client, conn, channel);
353 silc_free(channel_id);
359 idp = silc_id_payload_parse(tmp, tmp_len);
363 id_type = silc_id_payload_get_type(idp);
366 case SILC_ID_CHANNEL:
367 channel_id = silc_id_payload_get_id(idp);
368 channel = silc_client_get_channel_by_id(client, conn, channel_id);
369 silc_free(channel_id);
372 silc_id_payload_free(idp);
377 /* Now, send CMODE command for this channel. We send only this one
378 because this will return also error if we are not currently joined
379 on this channel, plus we get the channel mode. USERS and TOPIC
380 commands are called after this returns. */
382 SILC_LOG_DEBUG(("Sending CMODE"));
383 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
384 silc_client_command_reply_resume, 0,
386 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
387 conn->cmd_ident, 1, 1, tmp, tmp_len);
388 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
389 silc_client_command_resume_cmode, session);
392 silc_id_payload_free(idp);
394 if (cmd->status != SILC_STATUS_OK &&
395 cmd->status != SILC_STATUS_LIST_END)
398 /* Unregister this command reply */
399 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
400 silc_client_command_reply_resume,
405 session->channel_count--;
406 if (!session->channel_count)
407 RESUME_CALL_COMPLETION(client, session, FALSE);
410 /* Received cmode to channel entry */
412 SILC_CLIENT_CMD_FUNC(resume_cmode)
414 SilcClientResumeSession session = context;
415 SilcClientCommandReplyContext cmd = context2;
416 SilcClient client = session->client;
417 SilcClientConnection conn = session->conn;
419 SilcChannelID *channel_id;
420 SilcChannelEntry channel;
423 SILC_LOG_DEBUG(("Start"));
425 /* Unregister this command reply */
426 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
427 silc_client_command_reply_resume,
430 if (cmd->error != SILC_STATUS_OK)
433 /* Take Channel ID */
434 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
437 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
441 /* Get the channel entry */
442 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
445 /* Get channel mode */
446 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
448 SILC_GET32_MSB(channel->mode, tmp);
450 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
452 /* And now, we will send USERS to get users on the channel */
453 SILC_LOG_DEBUG(("Sending USERS"));
454 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
455 silc_client_command_reply_users_i, 0,
457 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
458 conn->cmd_ident, 1, 1, tmp, len);
459 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
460 silc_client_command_resume_users, session);
463 silc_free(channel_id);
467 session->channel_count--;
468 if (!session->channel_count)
469 RESUME_CALL_COMPLETION(client, session, FALSE);
472 /* Received users reply to a channel entry */
474 SILC_CLIENT_CMD_FUNC(resume_users)
476 SilcClientResumeSession session = context;
477 SilcClientCommandReplyContext cmd = context2;
478 SilcClient client = session->client;
479 SilcClientConnection conn = session->conn;
480 SilcBufferStruct client_id_list, client_mode_list;
482 SilcUInt32 tmp_len, list_count;
483 SilcChannelEntry channel;
484 SilcChannelID *channel_id = NULL;
486 SILC_LOG_DEBUG(("Start"));
488 /* Unregister this command reply */
489 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
490 silc_client_command_reply_users_i,
493 if (cmd->error != SILC_STATUS_OK)
497 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
502 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
508 /* Get the list count */
509 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
514 SILC_GET32_MSB(list_count, tmp);
516 /* Get Client ID list */
517 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
522 silc_buffer_set(&client_id_list, tmp, tmp_len);
524 /* Get client mode list */
525 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
530 silc_buffer_set(&client_mode_list, tmp, tmp_len);
532 /* Get channel entry */
533 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
537 /* Send fake JOIN command reply to application */
538 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
539 SILC_COMMAND_JOIN, cmd->status,
540 channel->channel_name, channel,
542 NULL, NULL, NULL, NULL,
543 channel->hmac, list_count,
544 &client_id_list, client_mode_list);
546 /* Send TOPIC for this channel to get the topic */
547 SILC_LOG_DEBUG(("Sending TOPIC"));
548 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
549 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
550 ++conn->cmd_ident, 1, 1, tmp, tmp_len);
552 /* Call the completion callback after we've got reply to all of
554 session->channel_count--;
555 if (!session->channel_count)
556 RESUME_CALL_COMPLETION(client, session, TRUE);
558 silc_free(channel_id);
562 silc_free(channel_id);
563 session->channel_count--;
564 if (!session->channel_count)
565 RESUME_CALL_COMPLETION(client, session, FALSE);