5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002, 2003 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 SILC_LOG_DEBUG(("Calling completion")); \
33 session->success = s; \
34 silc_schedule_task_add(client->schedule, 0, \
35 silc_client_resume_call_completion, session, \
36 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); \
39 /* Generates the session detachment data. This data can be used later
40 to resume back to the server. */
42 SilcBuffer silc_client_get_detach_data(SilcClient client,
43 SilcClientConnection conn)
46 SilcHashTableList htl;
50 SILC_LOG_DEBUG(("Creating detachment data"));
52 ch_count = silc_hash_table_count(conn->local_entry->channels);
54 /* Save the nickname, Client ID and user mode in SILC network */
55 detach = silc_buffer_alloc_size(2 + strlen(conn->nickname) +
56 2 + conn->local_id_data_len + 4 + 4);
57 silc_buffer_format(detach,
58 SILC_STR_UI_SHORT(strlen(conn->nickname)),
59 SILC_STR_UI_XNSTRING(conn->nickname,
60 strlen(conn->nickname)),
61 SILC_STR_UI_SHORT(conn->local_id_data_len),
62 SILC_STR_UI_XNSTRING(conn->local_id_data,
63 conn->local_id_data_len),
64 SILC_STR_UI_INT(conn->local_entry->mode),
65 SILC_STR_UI_INT(ch_count),
68 /* Save all joined channels */
69 silc_hash_table_list(conn->local_entry->channels, &htl);
70 while (silc_hash_table_get(&htl, NULL, (void **)&chu)) {
71 unsigned char *chid = silc_id_id2str(chu->channel->id, SILC_ID_CHANNEL);
72 SilcUInt16 chid_len = silc_id_get_len(chu->channel->id, SILC_ID_CHANNEL);
74 detach = silc_buffer_realloc(detach, detach->truelen + 2 +
75 strlen(chu->channel->channel_name) +
77 silc_buffer_pull(detach, detach->len);
78 silc_buffer_pull_tail(detach, 2 + strlen(chu->channel->channel_name) +
80 silc_buffer_format(detach,
81 SILC_STR_UI_SHORT(strlen(chu->channel->channel_name)),
82 SILC_STR_UI_XNSTRING(chu->channel->channel_name,
83 strlen(chu->channel->channel_name)),
84 SILC_STR_UI_SHORT(chid_len),
85 SILC_STR_UI_XNSTRING(chid, chid_len),
86 SILC_STR_UI_INT(chu->channel->mode),
90 silc_hash_table_list_reset(&htl);
92 silc_buffer_push(detach, detach->data - detach->head);
94 SILC_LOG_HEXDUMP(("Detach data"), detach->data, detach->len);
99 /* Processes the detachment data. This creates channels and other
100 stuff according the data found in the the connection parameters.
101 This doesn't actually resolve any detailed information from the
102 server. To do that call silc_client_resume_session function.
103 This returns the old detached session client ID. */
105 bool silc_client_process_detach_data(SilcClient client,
106 SilcClientConnection conn,
107 unsigned char **old_id,
108 SilcUInt16 *old_id_len)
110 SilcBufferStruct detach;
114 SILC_LOG_DEBUG(("Start"));
116 silc_free(conn->nickname);
117 silc_buffer_set(&detach, conn->internal->params.detach_data,
118 conn->internal->params.detach_data_len);
120 SILC_LOG_HEXDUMP(("Detach data"), detach.data, detach.len);
122 /* Take the old client ID from the detachment data */
123 len = silc_buffer_unformat(&detach,
124 SILC_STR_UI16_NSTRING_ALLOC(&conn->nickname,
126 SILC_STR_UI16_NSTRING_ALLOC(old_id, old_id_len),
127 SILC_STR_UI_INT(NULL),
128 SILC_STR_UI_INT(&ch_count),
133 silc_buffer_pull(&detach, len);
135 for (i = 0; i < ch_count; i++) {
140 SilcChannelID *channel_id;
141 SilcChannelEntry channel_entry;
143 len = silc_buffer_unformat(&detach,
144 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
145 SILC_STR_UI16_NSTRING(&chid, &chid_len),
146 SILC_STR_UI_INT(&ch_mode),
151 /* Add new channel */
152 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
153 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
154 if (!channel_entry) {
155 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
159 silc_free(channel_id);
162 silc_buffer_pull(&detach, len);
164 silc_buffer_push(&detach, detach.data - detach.head);
170 /* Resume session context */
173 SilcClientConnection conn;
174 SilcClientResumeSessionCallback callback;
176 SilcUInt32 channel_count;
177 SilcUInt32 *cmd_idents;
178 SilcUInt32 cmd_idents_count;
180 } *SilcClientResumeSession;
182 /* Generic command reply callback. */
184 SILC_CLIENT_CMD_REPLY_FUNC(resume)
186 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
187 SILC_LOG_DEBUG(("Start"));
188 SILC_CLIENT_PENDING_EXEC(cmd, silc_command_get(cmd->payload));
191 /* Special command reply callback for IDENTIFY callbacks. This calls
192 the pending callback for every returned command entry. */
194 SILC_CLIENT_CMD_REPLY_FUNC(resume_special)
196 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
199 SILC_LOG_DEBUG(("Start"));
200 for (i = 0; i < cmd->callbacks_count; i++)
201 if (cmd->callbacks[i].callback)
202 (*cmd->callbacks[i].callback)(cmd->callbacks[i].context, cmd);
205 /* Completion calling callback */
207 SILC_TASK_CALLBACK(silc_client_resume_call_completion)
209 SilcClientResumeSession session = context;
212 SILC_LOG_DEBUG(("Session completed"));
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 session->callback(session->client, session->conn, session->success,
222 memset(session, 'F', sizeof(*session));
226 /* This function is used to perform the resuming procedure after the
227 client has connected to the server properly and has received the
228 Client ID for the resumed session. This resolves all channels
229 that the resumed client is joined, joined users, users modes
230 and channel modes. The `callback' is called after this procedure
233 void silc_client_resume_session(SilcClient client,
234 SilcClientConnection conn,
235 SilcClientResumeSessionCallback callback,
238 SilcClientResumeSession session;
239 SilcIDCacheList list;
240 SilcIDCacheEntry entry;
241 SilcChannelEntry channel;
246 SILC_LOG_DEBUG(("Resuming detached session"));
248 session = silc_calloc(1, sizeof(*session));
250 callback(client, conn, FALSE, context);
253 session->client = client;
254 session->conn = conn;
255 session->callback = callback;
256 session->context = context;
258 /* First, send UMODE commandto get our own user mode in the network */
259 SILC_LOG_DEBUG(("Sending UMODE"));
260 tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
261 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
262 conn->cmd_ident, 1, 1, tmp->data, tmp->len);
263 silc_buffer_free(tmp);
265 /* Second, send IDENTIFY command of all channels we know about. These
266 are the channels we've joined to according our detachment data. */
267 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
268 unsigned char **res_argv = NULL;
269 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
271 session->channel_count = silc_idcache_list_count(list);
273 ret = silc_idcache_list_first(list, &entry);
275 channel = entry->context;
276 tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
277 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
278 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
280 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
282 res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
283 res_argv_lens[res_argc] = tmp->len;
284 res_argv_types[res_argc] = res_argc + 5;
286 silc_buffer_free(tmp);
287 ret = silc_idcache_list_next(list, &entry);
289 silc_idcache_list_free(list);
292 /* Send the IDENTIFY command */
293 SILC_LOG_DEBUG(("Sending IDENTIFY"));
294 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
295 silc_client_command_reply_resume_special,
296 0, ++conn->cmd_ident);
297 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
299 silc_client_command_resume_identify,
302 tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
303 res_argc, res_argv, res_argv_lens,
304 res_argv_types, conn->cmd_ident);
305 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
306 NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
308 session->cmd_idents = silc_realloc(session->cmd_idents,
309 sizeof(*session->cmd_idents) *
310 (session->cmd_idents_count + 1));
311 session->cmd_idents[session->cmd_idents_count] = conn->cmd_ident;
312 session->cmd_idents_count++;
314 for (i = 0; i < res_argc; i++)
315 silc_free(res_argv[i]);
317 silc_free(res_argv_lens);
318 silc_free(res_argv_types);
319 silc_buffer_free(tmp);
323 if (!session->channel_count)
324 RESUME_CALL_COMPLETION(client, session, TRUE);
326 /* Now, we wait for replies to come back and then continue with USERS,
327 CMODE and TOPIC commands. */
330 /* Received identify reply for a channel entry */
332 SILC_CLIENT_CMD_FUNC(resume_identify)
334 SilcClientResumeSession session = context;
335 SilcClientCommandReplyContext cmd = context2;
336 SilcClient client = session->client;
337 SilcClientConnection conn = session->conn;
340 SilcChannelEntry channel = NULL;
341 SilcChannelID *channel_id;
345 SILC_LOG_DEBUG(("Start"));
347 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
351 if (cmd->error != SILC_STATUS_OK) {
352 /* Delete unknown channel from our cache */
353 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
354 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
356 channel = silc_client_get_channel_by_id(client, conn, channel_id);
358 silc_client_del_channel(client, conn, channel);
359 silc_free(channel_id);
365 idp = silc_id_payload_parse(tmp, tmp_len);
369 id_type = silc_id_payload_get_type(idp);
372 case SILC_ID_CHANNEL:
373 channel_id = silc_id_payload_get_id(idp);
374 channel = silc_client_get_channel_by_id(client, conn, channel_id);
375 silc_free(channel_id);
378 silc_id_payload_free(idp);
383 /* Now, send CMODE command for this channel. We send only this one
384 because this will return also error if we are not currently joined
385 on this channel, plus we get the channel mode. USERS and TOPIC
386 commands are called after this returns. */
388 SILC_LOG_DEBUG(("Sending CMODE"));
389 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
390 silc_client_command_reply_resume, 0,
392 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
393 conn->cmd_ident, 1, 1, tmp, tmp_len);
394 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
395 silc_client_command_resume_cmode, session);
398 silc_id_payload_free(idp);
400 if (cmd->status != SILC_STATUS_OK &&
401 cmd->status != SILC_STATUS_LIST_END)
404 /* Unregister this command reply */
405 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
406 silc_client_command_reply_resume,
411 session->channel_count--;
412 if (!session->channel_count)
413 RESUME_CALL_COMPLETION(client, session, FALSE);
416 /* Received cmode to channel entry */
418 SILC_CLIENT_CMD_FUNC(resume_cmode)
420 SilcClientResumeSession session = context;
421 SilcClientCommandReplyContext cmd = context2;
422 SilcClient client = session->client;
423 SilcClientConnection conn = session->conn;
425 SilcChannelID *channel_id;
426 SilcChannelEntry channel;
429 SILC_LOG_DEBUG(("Start"));
431 /* Unregister this command reply */
432 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
433 silc_client_command_reply_resume,
436 if (cmd->error != SILC_STATUS_OK)
439 /* Take Channel ID */
440 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
443 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
447 /* Get the channel entry */
448 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
451 /* Get channel mode */
452 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
454 SILC_GET32_MSB(channel->mode, tmp);
456 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
458 /* And now, we will send USERS to get users on the channel */
459 SILC_LOG_DEBUG(("Sending USERS"));
460 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
461 silc_client_command_reply_users_i, 0,
463 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
464 conn->cmd_ident, 1, 1, tmp, len);
465 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
466 silc_client_command_resume_users, session);
469 silc_free(channel_id);
473 session->channel_count--;
474 if (!session->channel_count)
475 RESUME_CALL_COMPLETION(client, session, FALSE);
478 /* Received users reply to a channel entry */
480 SILC_CLIENT_CMD_FUNC(resume_users)
482 SilcClientResumeSession session = context;
483 SilcClientCommandReplyContext cmd = context2;
484 SilcClient client = session->client;
485 SilcClientConnection conn = session->conn;
486 SilcBufferStruct client_id_list, client_mode_list;
488 SilcUInt32 tmp_len, list_count;
489 SilcChannelEntry channel;
490 SilcChannelID *channel_id = NULL;
492 SILC_LOG_DEBUG(("Start"));
494 /* Unregister this command reply */
495 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
496 silc_client_command_reply_users_i,
499 if (cmd->error != SILC_STATUS_OK)
503 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
505 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
508 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
510 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
514 /* Get the list count */
515 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
517 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
520 SILC_GET32_MSB(list_count, tmp);
522 /* Get Client ID list */
523 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
525 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
528 silc_buffer_set(&client_id_list, tmp, tmp_len);
530 /* Get client mode list */
531 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
533 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
536 silc_buffer_set(&client_mode_list, tmp, tmp_len);
538 /* Get channel entry */
539 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
543 /* Send fake JOIN command reply to application */
544 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
545 SILC_COMMAND_JOIN, cmd->status,
546 channel->channel_name, channel,
548 NULL, NULL, NULL, NULL,
549 channel->hmac, list_count,
550 &client_id_list, client_mode_list);
552 /* Send TOPIC for this channel to get the topic */
553 SILC_LOG_DEBUG(("Sending TOPIC"));
554 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
555 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
556 ++conn->cmd_ident, 1, 1, tmp, tmp_len);
558 /* Call the completion callback after we've got reply to all of
560 session->channel_count--;
561 if (!session->channel_count)
562 RESUME_CALL_COMPLETION(client, session, TRUE);
564 silc_free(channel_id);
568 silc_free(channel_id);
569 session->channel_count--;
570 if (!session->channel_count)
571 RESUME_CALL_COMPLETION(client, session, FALSE);