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 /* Generates the session detachment data. This data can be used later
31 to resume back to the server. */
33 SilcBuffer silc_client_get_detach_data(SilcClient client,
34 SilcClientConnection conn)
37 SilcHashTableList htl;
41 SILC_LOG_DEBUG(("Creating detachment data"));
43 ch_count = silc_hash_table_count(conn->local_entry->channels);
45 /* Save the nickname, Client ID and user mode in SILC network */
46 detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) +
47 2 + conn->local_id_data_len + 4 + 4);
48 silc_buffer_format(detach,
49 SILC_STR_UI_SHORT(strlen(conn->nickname)),
50 SILC_STR_UI_XNSTRING(conn->nickname,
51 strlen(conn->nickname)),
52 SILC_STR_UI_SHORT(conn->local_id_data_len),
53 SILC_STR_UI_XNSTRING(conn->local_id_data,
54 conn->local_id_data_len),
55 SILC_STR_UI_INT(conn->local_entry->mode),
56 SILC_STR_UI_INT(ch_count),
59 /* Save all joined channels */
60 silc_hash_table_list(conn->local_entry->channels, &htl);
61 while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
62 unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL);
63 SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL);
65 detach = silc_buffer_realloc(detach, detach->truelen + 2 +
66 strlen(chu->channel->channel_name) +
68 silc_buffer_pull(detach, detach->len);
69 silc_buffer_pull_tail(detach, 2 + strlen(chu->channel->channel_name) +
71 silc_buffer_format(detach,
72 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
73 SILC_STR_UI_XNSTRING(chu->channel->channel_name,
74 strlen(chu->channel->channel_name)),
75 SILC_STR_UI_SHORT(chid_len),
76 SILC_STR_UI_XNSTRING(chid, chid_len),
77 SILC_STR_UI_INT(chu->channel->mode),
81 silc_hash_table_list_reset(&htl);
83 silc_buffer_push(detach, detach->data - detach->head);
85 SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len);
90 /* Processes the detachment data. This creates channels and other
91 stuff according the data found in the the connection parameters.
92 This doesn't actually resolve any detailed information from the
93 server. To do that call silc_client_resume_session function.
94 This returns the old detached session client ID. */
96 bool silc_client_process_detach_data(SilcClient client,
97 SilcClientConnection conn,
98 unsigned char **old_id,
99 SilcUInt16 *old_id_len)
101 SilcBufferStruct detach;
105 SILC_LOG_DEBUG(("Start"));
107 silc_free(conn->nickname);
108 silc_buffer_set(&detach, conn->params.detach_data,
109 conn->params.detach_data_len);
111 SILC_LOG_HEXDUMP(("Detach data"), detach.data, detach.len);
113 /* Take the old client ID from the detachment data */
114 len = silc_buffer_unformat(&detach,
115 SILC_STR_UI16_NSTRING_ALLOC(&conn->nickname,
117 SILC_STR_UI16_NSTRING_ALLOC(old_id, old_id_len),
118 SILC_STR_UI_INT(NULL),
119 SILC_STR_UI_INT(&ch_count),
124 silc_buffer_pull(&detach, len);
126 for (i = 0; i < ch_count; i++) {
131 SilcChannelID *channel_id;
132 SilcChannelEntry channel_entry;
134 len = silc_buffer_unformat(&detach,
135 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
136 SILC_STR_UI16_NSTRING(&chid, &chid_len),
137 SILC_STR_UI_INT(&ch_mode),
142 /* Add new channel */
143 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
144 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
145 if (!channel_entry) {
146 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
150 silc_free(channel_id);
153 silc_buffer_pull(&detach, len);
155 silc_buffer_push(&detach, detach.data - detach.head);
160 /* Generic command reply callback */
162 SILC_CLIENT_CMD_REPLY_FUNC(resume)
164 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
166 SILC_LOG_DEBUG(("Start"));
169 (*cmd->callback)(cmd->context, cmd);
172 /* Resume session context */
175 SilcClientConnection conn;
176 SilcClientResumeSessionCallback callback;
178 SilcUInt32 channel_count;
179 } *SilcClientResumeSession;
181 /* This function is used to perform the resuming procedure after the
182 client has connected to the server properly and has received the
183 Client ID for the resumed session. This resolves all channels
184 that the resumed client is joined, joined users, users modes
185 and channel modes. The `callback' is called after this procedure
188 void silc_client_resume_session(SilcClient client,
189 SilcClientConnection conn,
190 SilcClientResumeSessionCallback callback,
193 SilcClientResumeSession session;
194 SilcIDCacheList list;
195 SilcIDCacheEntry entry;
196 SilcChannelEntry channel;
201 SILC_LOG_DEBUG(("Resuming detached session"));
203 session = silc_calloc(1, sizeof(*session));
205 callback(client, conn, FALSE, context);
208 session->client = client;
209 session->conn = conn;
210 session->callback = callback;
211 session->context = context;
213 /* First, send UMODE commandto get our own user mode in the network */
214 SILC_LOG_DEBUG(("Sending UMODE"));
215 tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
216 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
217 conn->cmd_ident, 1, 1, tmp->data, tmp->len);
218 silc_buffer_free(tmp);
220 /* Second, send IDENTIFY command of all channels we know about. These
221 are the channels we've joined to according our detachment data. */
222 if (silc_idcache_get_all(conn->channel_cache, &list)) {
223 unsigned char **res_argv = NULL;
224 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
226 session->channel_count = silc_idcache_list_count(list);
228 ret = silc_idcache_list_first(list, &entry);
230 channel = entry->context;
231 tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
232 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
233 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
235 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
237 res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
238 res_argv_lens[res_argc] = tmp->len;
239 res_argv_types[res_argc] = res_argc + 5;
241 silc_buffer_free(tmp);
242 ret = silc_idcache_list_next(list, &entry);
244 silc_idcache_list_free(list);
247 /* Send the IDENTIFY command */
248 SILC_LOG_DEBUG(("Sending IDENTIFY"));
249 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
250 silc_client_command_reply_resume,
251 0, ++conn->cmd_ident);
252 tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
253 res_argc, res_argv, res_argv_lens,
254 res_argv_types, conn->cmd_ident);
255 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
257 silc_client_command_resume_identify,
259 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
260 NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
262 for (i = 0; i < res_argc; i++)
263 silc_free(res_argv[i]);
265 silc_free(res_argv_lens);
266 silc_free(res_argv_types);
267 silc_buffer_free(tmp);
271 /* Now, we wait for replies to come back and then continue with USERS,
272 CMODE and TOPIC commands. */
275 /* Received identify reply for a channel entry */
277 SILC_CLIENT_CMD_FUNC(resume_identify)
279 SilcClientResumeSession session = context;
280 SilcClientCommandReplyContext cmd = context2;
281 SilcClient client = session->client;
282 SilcClientConnection conn = session->conn;
285 SilcChannelEntry channel = NULL;
286 SilcChannelID *channel_id;
290 SILC_LOG_DEBUG(("Start"));
292 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
296 if (cmd->error != SILC_STATUS_OK) {
297 /* Delete unknown channel from our cache */
298 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
299 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
301 channel = silc_client_get_channel_by_id(client, conn, channel_id);
303 silc_client_del_channel(client, conn, channel);
304 silc_free(channel_id);
310 idp = silc_id_payload_parse(tmp, tmp_len);
314 id_type = silc_id_payload_get_type(idp);
317 case SILC_ID_CHANNEL:
318 channel_id = silc_id_payload_get_id(idp);
319 channel = silc_client_get_channel_by_id(client, conn, channel_id);
320 silc_free(channel_id);
323 silc_id_payload_free(idp);
328 /* Now, send CMODE command for this channel. We send only this one
329 because this will return also error if we are not currently joined
330 on this channel, plus we get the channel mode. USERS and TOPIC
331 commands are called after this returns. */
333 SILC_LOG_DEBUG(("Sending CMODE"));
334 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
335 silc_client_command_reply_resume, 0,
337 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
338 conn->cmd_ident, 1, 1, tmp, tmp_len);
339 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
340 silc_client_command_resume_cmode, session);
343 silc_id_payload_free(idp);
345 if (cmd->status != SILC_STATUS_OK &&
346 cmd->status != SILC_STATUS_LIST_END)
349 /* Unregister this command reply */
350 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
351 silc_client_command_reply_resume,
356 session->channel_count--;
357 if (!session->channel_count)
358 session->callback(session->client, session->conn, FALSE,
362 /* Received cmode to channel entry */
364 SILC_CLIENT_CMD_FUNC(resume_cmode)
366 SilcClientResumeSession session = context;
367 SilcClientCommandReplyContext cmd = context2;
368 SilcClient client = session->client;
369 SilcClientConnection conn = session->conn;
371 SilcChannelID *channel_id;
372 SilcChannelEntry channel;
375 SILC_LOG_DEBUG(("Start"));
377 /* Unregister this command reply */
378 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
379 silc_client_command_reply_resume,
382 if (cmd->error != SILC_STATUS_OK)
385 /* Take Channel ID */
386 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
389 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
393 /* Get the channel entry */
394 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
397 /* Get channel mode */
398 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
400 SILC_GET32_MSB(channel->mode, tmp);
402 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
404 /* And now, we will send USERS to get users on the channel */
405 SILC_LOG_DEBUG(("Sending USERS"));
406 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
407 silc_client_command_reply_users_i, 0,
409 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
410 conn->cmd_ident, 1, 1, tmp, len);
411 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
412 silc_client_command_resume_users, session);
415 silc_free(channel_id);
419 session->channel_count--;
420 if (!session->channel_count)
421 session->callback(session->client, session->conn, FALSE,
425 /* Received users reply to a channel entry */
427 SILC_CLIENT_CMD_FUNC(resume_users)
429 SilcClientResumeSession session = context;
430 SilcClientCommandReplyContext cmd = context2;
431 SilcClient client = session->client;
432 SilcClientConnection conn = session->conn;
433 SilcBufferStruct client_id_list, client_mode_list;
435 SilcUInt32 tmp_len, list_count;
436 SilcChannelEntry channel;
437 SilcChannelID *channel_id = NULL;
439 SILC_LOG_DEBUG(("Start"));
441 /* Unregister this command reply */
442 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
443 silc_client_command_reply_users_i,
446 if (cmd->error != SILC_STATUS_OK)
450 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
455 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
461 /* Get the list count */
462 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
467 SILC_GET32_MSB(list_count, tmp);
469 /* Get Client ID list */
470 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
475 silc_buffer_set(&client_id_list, tmp, tmp_len);
477 /* Get client mode list */
478 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
483 silc_buffer_set(&client_mode_list, tmp, tmp_len);
485 /* Get channel entry */
486 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
490 /* Send fake JOIN command reply to application */
491 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
492 SILC_COMMAND_JOIN, cmd->status,
493 channel->channel_name, channel,
495 NULL, NULL, NULL, NULL,
496 channel->hmac, list_count,
497 &client_id_list, client_mode_list);
499 /* Send TOPIC for this channel to get the topic */
500 SILC_LOG_DEBUG(("Sending TOPIC"));
501 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
502 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
503 conn->cmd_ident, 1, 1, tmp, tmp_len);
505 /* Call the completion callback after we've got reply to all of
507 session->channel_count--;
508 if (!session->channel_count)
509 session->callback(session->client, session->conn, TRUE,
512 silc_free(channel_id);
516 silc_free(channel_id);
517 session->channel_count--;
518 if (!session->channel_count)
519 session->callback(session->client, session->conn, FALSE,