5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002, 2004 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.
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 SilcBool silc_client_process_detach_data(SilcClient client,
106 SilcClientConnection conn,
107 unsigned char **old_id,
108 SilcUInt16 *old_id_len)
110 SilcBufferStruct detach;
115 SILC_LOG_DEBUG(("Start"));
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);
125 /* Take the old client ID from the detachment data */
126 len = silc_buffer_unformat(&detach,
127 SILC_STR_UI16_NSTRING_ALLOC(&newnick,
129 SILC_STR_UI16_NSTRING_ALLOC(old_id, old_id_len),
130 SILC_STR_UI_INT(NULL),
131 SILC_STR_UI_INT(&ch_count),
135 if (!newnick || !(*old_id) || !(*old_id_len))
138 silc_free(conn->nickname);
139 conn->nickname = newnick;
141 silc_buffer_pull(&detach, len);
143 for (i = 0; i < ch_count; i++) {
148 SilcChannelID *channel_id;
149 SilcChannelEntry channel_entry;
151 len = silc_buffer_unformat(&detach,
152 SILC_STR_UI16_NSTRING_ALLOC(&channel, NULL),
153 SILC_STR_UI16_NSTRING(&chid, &chid_len),
154 SILC_STR_UI_INT(&ch_mode),
159 /* Add new channel */
160 channel_id = silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL);
161 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
162 if (!channel_entry) {
163 channel_entry = silc_client_add_channel(client, conn, channel, ch_mode,
167 silc_free(channel_id);
170 silc_buffer_pull(&detach, len);
172 silc_buffer_push(&detach, detach.data - detach.head);
178 /* Resume session context */
181 SilcClientConnection conn;
182 SilcClientResumeSessionCallback callback;
184 SilcUInt32 channel_count;
185 SilcUInt32 *cmd_idents;
186 SilcUInt32 cmd_idents_count;
188 } *SilcClientResumeSession;
190 /* Generic command reply callback. */
192 SILC_CLIENT_CMD_REPLY_FUNC(resume)
194 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
195 SILC_LOG_DEBUG(("Start"));
196 SILC_CLIENT_PENDING_EXEC(cmd, silc_command_get(cmd->payload));
199 /* Special command reply callback for IDENTIFY callbacks. This calls
200 the pending callback for every returned command entry. */
202 SILC_CLIENT_CMD_REPLY_FUNC(resume_special)
204 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
207 SILC_LOG_DEBUG(("Start"));
208 for (i = 0; i < cmd->callbacks_count; i++)
209 if (cmd->callbacks[i].callback)
210 (*cmd->callbacks[i].callback)(cmd->callbacks[i].context, cmd);
213 /* Completion calling callback */
215 SILC_TASK_CALLBACK(silc_client_resume_call_completion)
217 SilcClientResumeSession session = context;
220 SILC_LOG_DEBUG(("Session completed"));
222 for (i = 0; i < session->cmd_idents_count; i++)
223 silc_client_command_pending_del(session->conn, SILC_COMMAND_IDENTIFY,
224 session->cmd_idents[i]);
225 silc_free(session->cmd_idents);
227 session->callback(session->client, session->conn, session->success,
230 memset(session, 'F', sizeof(*session));
234 /* This function is used to perform the resuming procedure after the
235 client has connected to the server properly and has received the
236 Client ID for the resumed session. This resolves all channels
237 that the resumed client is joined, joined users, users modes
238 and channel modes. The `callback' is called after this procedure
241 void silc_client_resume_session(SilcClient client,
242 SilcClientConnection conn,
243 SilcClientResumeSessionCallback callback,
246 SilcClientResumeSession session;
247 SilcIDCacheList list;
248 SilcIDCacheEntry entry;
249 SilcChannelEntry channel;
254 SILC_LOG_DEBUG(("Resuming detached session"));
256 session = silc_calloc(1, sizeof(*session));
258 callback(client, conn, FALSE, context);
261 session->client = client;
262 session->conn = conn;
263 session->callback = callback;
264 session->context = context;
266 /* First, send UMODE commandto get our own user mode in the network */
267 SILC_LOG_DEBUG(("Sending UMODE"));
268 tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
269 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
270 conn->cmd_ident, 1, 1, tmp->data, tmp->len);
271 silc_buffer_free(tmp);
273 /* Second, send IDENTIFY command of all channels we know about. These
274 are the channels we've joined to according our detachment data. */
275 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
276 unsigned char **res_argv = NULL;
277 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
279 session->channel_count = silc_idcache_list_count(list);
281 ret = silc_idcache_list_first(list, &entry);
283 channel = entry->context;
284 tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
285 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
286 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
288 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
290 res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
291 res_argv_lens[res_argc] = tmp->len;
292 res_argv_types[res_argc] = res_argc + 5;
294 silc_buffer_free(tmp);
295 ret = silc_idcache_list_next(list, &entry);
297 silc_idcache_list_free(list);
300 /* Send the IDENTIFY command */
301 SILC_LOG_DEBUG(("Sending IDENTIFY"));
302 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
303 silc_client_command_reply_resume_special,
304 0, ++conn->cmd_ident);
305 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
307 silc_client_command_resume_identify,
310 tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
311 res_argc, res_argv, res_argv_lens,
312 res_argv_types, conn->cmd_ident);
313 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
314 NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
316 session->cmd_idents = silc_realloc(session->cmd_idents,
317 sizeof(*session->cmd_idents) *
318 (session->cmd_idents_count + 1));
319 session->cmd_idents[session->cmd_idents_count] = conn->cmd_ident;
320 session->cmd_idents_count++;
322 for (i = 0; i < res_argc; i++)
323 silc_free(res_argv[i]);
325 silc_free(res_argv_lens);
326 silc_free(res_argv_types);
327 silc_buffer_free(tmp);
331 if (!session->channel_count)
332 RESUME_CALL_COMPLETION(client, session, TRUE);
334 /* Now, we wait for replies to come back and then continue with USERS,
335 CMODE and TOPIC commands. */
338 /* Received identify reply for a channel entry */
340 SILC_CLIENT_CMD_FUNC(resume_identify)
342 SilcClientResumeSession session = context;
343 SilcClientCommandReplyContext cmd = context2;
344 SilcClient client = session->client;
345 SilcClientConnection conn = session->conn;
348 SilcChannelEntry channel = NULL;
349 SilcChannelID *channel_id;
353 SILC_LOG_DEBUG(("Start"));
355 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
359 if (cmd->error != SILC_STATUS_OK) {
360 /* Delete unknown channel from our cache */
361 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
362 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
364 channel = silc_client_get_channel_by_id(client, conn, channel_id);
366 silc_client_del_channel(client, conn, channel);
367 silc_free(channel_id);
373 idp = silc_id_payload_parse(tmp, tmp_len);
377 id_type = silc_id_payload_get_type(idp);
380 case SILC_ID_CHANNEL:
381 channel_id = silc_id_payload_get_id(idp);
382 channel = silc_client_get_channel_by_id(client, conn, channel_id);
383 silc_free(channel_id);
386 silc_id_payload_free(idp);
391 /* Now, send CMODE command for this channel. We send only this one
392 because this will return also error if we are not currently joined
393 on this channel, plus we get the channel mode. USERS and TOPIC
394 commands are called after this returns. */
396 SILC_LOG_DEBUG(("Sending CMODE"));
397 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
398 silc_client_command_reply_resume, 0,
400 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
401 conn->cmd_ident, 1, 1, tmp, tmp_len);
402 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
403 silc_client_command_resume_cmode, session);
406 silc_id_payload_free(idp);
408 if (cmd->status != SILC_STATUS_OK &&
409 cmd->status != SILC_STATUS_LIST_END)
412 /* Unregister this command reply */
413 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
414 silc_client_command_reply_resume,
419 session->channel_count--;
420 if (!session->channel_count)
421 RESUME_CALL_COMPLETION(client, session, FALSE);
424 /* Received cmode to channel entry */
426 SILC_CLIENT_CMD_FUNC(resume_cmode)
428 SilcClientResumeSession session = context;
429 SilcClientCommandReplyContext cmd = context2;
430 SilcClient client = session->client;
431 SilcClientConnection conn = session->conn;
433 SilcChannelID *channel_id;
434 SilcChannelEntry channel;
437 SILC_LOG_DEBUG(("Start"));
439 /* Unregister this command reply */
440 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
441 silc_client_command_reply_resume,
444 if (cmd->error != SILC_STATUS_OK)
447 /* Take Channel ID */
448 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
451 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
455 /* Get the channel entry */
456 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
459 /* Get channel mode */
460 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
462 SILC_GET32_MSB(channel->mode, tmp);
464 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
466 /* And now, we will send USERS to get users on the channel */
467 SILC_LOG_DEBUG(("Sending USERS"));
468 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
469 silc_client_command_reply_users_i, 0,
471 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
472 conn->cmd_ident, 1, 1, tmp, len);
473 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
474 silc_client_command_resume_users, session);
477 silc_free(channel_id);
481 session->channel_count--;
482 if (!session->channel_count)
483 RESUME_CALL_COMPLETION(client, session, FALSE);
486 /* Received users reply to a channel entry */
488 SILC_CLIENT_CMD_FUNC(resume_users)
490 SilcClientResumeSession session = context;
491 SilcClientCommandReplyContext cmd = context2;
492 SilcClient client = session->client;
493 SilcClientConnection conn = session->conn;
494 SilcBufferStruct client_id_list, client_mode_list;
496 SilcUInt32 tmp_len, list_count;
497 SilcChannelEntry channel;
498 SilcChannelID *channel_id = NULL;
500 SILC_LOG_DEBUG(("Start"));
502 /* Unregister this command reply */
503 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
504 silc_client_command_reply_users_i,
507 if (cmd->error != SILC_STATUS_OK)
511 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
513 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
516 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
518 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
522 /* Get the list count */
523 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
525 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
528 SILC_GET32_MSB(list_count, tmp);
530 /* Get Client ID list */
531 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
533 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
536 silc_buffer_set(&client_id_list, tmp, tmp_len);
538 /* Get client mode list */
539 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
541 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
544 silc_buffer_set(&client_mode_list, tmp, tmp_len);
546 /* Get channel entry */
547 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
551 /* Send fake JOIN command reply to application */
552 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
553 SILC_COMMAND_JOIN, cmd->status,
554 channel->channel_name, channel,
556 NULL, NULL, NULL, NULL,
557 channel->hmac, list_count,
558 &client_id_list, client_mode_list);
560 /* Send TOPIC for this channel to get the topic */
561 SILC_LOG_DEBUG(("Sending TOPIC"));
562 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
563 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
564 ++conn->cmd_ident, 1, 1, tmp, tmp_len);
566 /* Call the completion callback after we've got reply to all of
568 session->channel_count--;
569 if (!session->channel_count)
570 RESUME_CALL_COMPLETION(client, session, TRUE);
572 silc_free(channel_id);
576 silc_free(channel_id);
577 session->channel_count--;
578 if (!session->channel_count)
579 RESUME_CALL_COMPLETION(client, session, FALSE);