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;
177 } *SilcClientResumeSession;
179 /* Generic command reply callback */
181 SILC_CLIENT_CMD_REPLY_FUNC(resume)
183 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
185 SILC_LOG_DEBUG(("Start"));
188 (*cmd->callback)(cmd->context, cmd);
191 /* Completion calling callback */
193 SILC_TASK_CALLBACK(silc_client_resume_call_completion)
195 SilcClientResumeSession session = context;
196 session->callback(session->client, session->conn, session->success,
200 /* This function is used to perform the resuming procedure after the
201 client has connected to the server properly and has received the
202 Client ID for the resumed session. This resolves all channels
203 that the resumed client is joined, joined users, users modes
204 and channel modes. The `callback' is called after this procedure
207 void silc_client_resume_session(SilcClient client,
208 SilcClientConnection conn,
209 SilcClientResumeSessionCallback callback,
212 SilcClientResumeSession session;
213 SilcIDCacheList list;
214 SilcIDCacheEntry entry;
215 SilcChannelEntry channel;
220 SILC_LOG_DEBUG(("Resuming detached session"));
222 session = silc_calloc(1, sizeof(*session));
224 callback(client, conn, FALSE, context);
227 session->client = client;
228 session->conn = conn;
229 session->callback = callback;
230 session->context = context;
232 /* First, send UMODE commandto get our own user mode in the network */
233 SILC_LOG_DEBUG(("Sending UMODE"));
234 tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
235 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
236 conn->cmd_ident, 1, 1, tmp->data, tmp->len);
237 silc_buffer_free(tmp);
239 /* Second, send IDENTIFY command of all channels we know about. These
240 are the channels we've joined to according our detachment data. */
241 if (silc_idcache_get_all(conn->channel_cache, &list)) {
242 unsigned char **res_argv = NULL;
243 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
245 session->channel_count = silc_idcache_list_count(list);
247 ret = silc_idcache_list_first(list, &entry);
249 channel = entry->context;
250 tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
251 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
252 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
254 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
256 res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
257 res_argv_lens[res_argc] = tmp->len;
258 res_argv_types[res_argc] = res_argc + 5;
260 silc_buffer_free(tmp);
261 ret = silc_idcache_list_next(list, &entry);
263 silc_idcache_list_free(list);
266 /* Send the IDENTIFY command */
267 SILC_LOG_DEBUG(("Sending IDENTIFY"));
268 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
269 silc_client_command_reply_resume,
270 0, ++conn->cmd_ident);
271 tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
272 res_argc, res_argv, res_argv_lens,
273 res_argv_types, conn->cmd_ident);
274 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
276 silc_client_command_resume_identify,
278 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
279 NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
281 for (i = 0; i < res_argc; i++)
282 silc_free(res_argv[i]);
284 silc_free(res_argv_lens);
285 silc_free(res_argv_types);
286 silc_buffer_free(tmp);
290 /* Now, we wait for replies to come back and then continue with USERS,
291 CMODE and TOPIC commands. */
294 /* Received identify reply for a channel entry */
296 SILC_CLIENT_CMD_FUNC(resume_identify)
298 SilcClientResumeSession session = context;
299 SilcClientCommandReplyContext cmd = context2;
300 SilcClient client = session->client;
301 SilcClientConnection conn = session->conn;
304 SilcChannelEntry channel = NULL;
305 SilcChannelID *channel_id;
309 SILC_LOG_DEBUG(("Start"));
311 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
315 if (cmd->error != SILC_STATUS_OK) {
316 /* Delete unknown channel from our cache */
317 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
318 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
320 channel = silc_client_get_channel_by_id(client, conn, channel_id);
322 silc_client_del_channel(client, conn, channel);
323 silc_free(channel_id);
329 idp = silc_id_payload_parse(tmp, tmp_len);
333 id_type = silc_id_payload_get_type(idp);
336 case SILC_ID_CHANNEL:
337 channel_id = silc_id_payload_get_id(idp);
338 channel = silc_client_get_channel_by_id(client, conn, channel_id);
339 silc_free(channel_id);
342 silc_id_payload_free(idp);
347 /* Now, send CMODE command for this channel. We send only this one
348 because this will return also error if we are not currently joined
349 on this channel, plus we get the channel mode. USERS and TOPIC
350 commands are called after this returns. */
352 SILC_LOG_DEBUG(("Sending CMODE"));
353 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
354 silc_client_command_reply_resume, 0,
356 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
357 conn->cmd_ident, 1, 1, tmp, tmp_len);
358 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
359 silc_client_command_resume_cmode, session);
362 silc_id_payload_free(idp);
364 if (cmd->status != SILC_STATUS_OK &&
365 cmd->status != SILC_STATUS_LIST_END)
368 /* Unregister this command reply */
369 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
370 silc_client_command_reply_resume,
375 session->channel_count--;
376 if (!session->channel_count)
377 RESUME_CALL_COMPLETION(client, session, FALSE);
380 /* Received cmode to channel entry */
382 SILC_CLIENT_CMD_FUNC(resume_cmode)
384 SilcClientResumeSession session = context;
385 SilcClientCommandReplyContext cmd = context2;
386 SilcClient client = session->client;
387 SilcClientConnection conn = session->conn;
389 SilcChannelID *channel_id;
390 SilcChannelEntry channel;
393 SILC_LOG_DEBUG(("Start"));
395 /* Unregister this command reply */
396 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
397 silc_client_command_reply_resume,
400 if (cmd->error != SILC_STATUS_OK)
403 /* Take Channel ID */
404 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
407 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
411 /* Get the channel entry */
412 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
415 /* Get channel mode */
416 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
418 SILC_GET32_MSB(channel->mode, tmp);
420 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
422 /* And now, we will send USERS to get users on the channel */
423 SILC_LOG_DEBUG(("Sending USERS"));
424 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
425 silc_client_command_reply_users_i, 0,
427 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
428 conn->cmd_ident, 1, 1, tmp, len);
429 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
430 silc_client_command_resume_users, session);
433 silc_free(channel_id);
437 session->channel_count--;
438 if (!session->channel_count)
439 RESUME_CALL_COMPLETION(client, session, FALSE);
442 /* Received users reply to a channel entry */
444 SILC_CLIENT_CMD_FUNC(resume_users)
446 SilcClientResumeSession session = context;
447 SilcClientCommandReplyContext cmd = context2;
448 SilcClient client = session->client;
449 SilcClientConnection conn = session->conn;
450 SilcBufferStruct client_id_list, client_mode_list;
452 SilcUInt32 tmp_len, list_count;
453 SilcChannelEntry channel;
454 SilcChannelID *channel_id = NULL;
456 SILC_LOG_DEBUG(("Start"));
458 /* Unregister this command reply */
459 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
460 silc_client_command_reply_users_i,
463 if (cmd->error != SILC_STATUS_OK)
467 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
472 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
478 /* Get the list count */
479 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
484 SILC_GET32_MSB(list_count, tmp);
486 /* Get Client ID list */
487 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
492 silc_buffer_set(&client_id_list, tmp, tmp_len);
494 /* Get client mode list */
495 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
500 silc_buffer_set(&client_mode_list, tmp, tmp_len);
502 /* Get channel entry */
503 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
507 /* Send fake JOIN command reply to application */
508 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
509 SILC_COMMAND_JOIN, cmd->status,
510 channel->channel_name, channel,
512 NULL, NULL, NULL, NULL,
513 channel->hmac, list_count,
514 &client_id_list, client_mode_list);
516 /* Send TOPIC for this channel to get the topic */
517 SILC_LOG_DEBUG(("Sending TOPIC"));
518 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
519 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
520 conn->cmd_ident, 1, 1, tmp, tmp_len);
522 /* Call the completion callback after we've got reply to all of
524 session->channel_count--;
525 if (!session->channel_count)
526 RESUME_CALL_COMPLETION(client, session, TRUE);
528 silc_free(channel_id);
532 silc_free(channel_id);
533 session->channel_count--;
534 if (!session->channel_count)
535 RESUME_CALL_COMPLETION(client, session, FALSE);