5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002, 2004, 2006 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 /* Generic command reply callback. */
41 SILC_CLIENT_CMD_REPLY_FUNC(resume)
43 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
44 SILC_LOG_DEBUG(("Start"));
45 SILC_CLIENT_PENDING_EXEC(cmd, silc_command_get(cmd->payload));
48 /* Special command reply callback for IDENTIFY callbacks. This calls
49 the pending callback for every returned command entry. */
51 SILC_CLIENT_CMD_REPLY_FUNC(resume_special)
53 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
56 SILC_LOG_DEBUG(("Start"));
57 for (i = 0; i < cmd->callbacks_count; i++)
58 if (cmd->callbacks[i].callback)
59 (*cmd->callbacks[i].callback)(cmd->callbacks[i].context, cmd);
62 /* Completion calling callback */
64 SILC_TASK_CALLBACK(silc_client_resume_call_completion)
66 SilcClientResumeSession session = context;
69 SILC_LOG_DEBUG(("Session completed"));
71 for (i = 0; i < session->cmd_idents_count; i++)
72 silc_client_command_pending_del(session->conn, SILC_COMMAND_IDENTIFY,
73 session->cmd_idents[i]);
74 silc_free(session->cmd_idents);
76 session->callback(session->client, session->conn, session->success,
79 memset(session, 'F', sizeof(*session));
83 /* This function is used to perform the resuming procedure after the
84 client has connected to the server properly and has received the
85 Client ID for the resumed session. This resolves all channels
86 that the resumed client is joined, joined users, users modes
87 and channel modes. The `callback' is called after this procedure
90 void silc_client_resume_session(SilcClient client,
91 SilcClientConnection conn,
92 SilcClientResumeSessionCallback callback,
95 SilcClientResumeSession session;
97 SilcIDCacheEntry entry;
98 SilcChannelEntry channel;
103 SILC_LOG_DEBUG(("Resuming detached session"));
105 session = silc_calloc(1, sizeof(*session));
107 callback(client, conn, FALSE, context);
110 session->client = client;
111 session->conn = conn;
112 session->callback = callback;
113 session->context = context;
115 /* First, send UMODE commandto get our own user mode in the network */
116 SILC_LOG_DEBUG(("Sending UMODE"));
117 tmp = silc_id_payload_encode(conn->local_entry->id, SILC_ID_CLIENT);
118 silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
119 conn->cmd_ident, 1, 1, tmp->data, tmp->len);
120 silc_buffer_free(tmp);
122 /* Second, send IDENTIFY command of all channels we know about. These
123 are the channels we've joined to according our detachment data. */
124 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
125 unsigned char **res_argv = NULL;
126 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
128 session->channel_count = silc_idcache_list_count(list);
130 ret = silc_idcache_list_first(list, &entry);
132 channel = entry->context;
133 tmp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
134 res_argv = silc_realloc(res_argv, sizeof(*res_argv) * (res_argc + 1));
135 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
137 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
139 res_argv[res_argc] = silc_memdup(tmp->data, tmp->len);
140 res_argv_lens[res_argc] = tmp->len;
141 res_argv_types[res_argc] = res_argc + 5;
143 silc_buffer_free(tmp);
144 ret = silc_idcache_list_next(list, &entry);
146 silc_idcache_list_free(list);
149 /* Send the IDENTIFY command */
150 SILC_LOG_DEBUG(("Sending IDENTIFY"));
151 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
152 silc_client_command_reply_resume_special,
153 0, ++conn->cmd_ident);
154 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
156 silc_client_command_resume_identify,
159 tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
160 res_argc, res_argv, res_argv_lens,
161 res_argv_types, conn->cmd_ident);
162 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
163 NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
165 session->cmd_idents = silc_realloc(session->cmd_idents,
166 sizeof(*session->cmd_idents) *
167 (session->cmd_idents_count + 1));
168 session->cmd_idents[session->cmd_idents_count] = conn->cmd_ident;
169 session->cmd_idents_count++;
171 for (i = 0; i < res_argc; i++)
172 silc_free(res_argv[i]);
174 silc_free(res_argv_lens);
175 silc_free(res_argv_types);
176 silc_buffer_free(tmp);
180 if (!session->channel_count)
181 RESUME_CALL_COMPLETION(client, session, TRUE);
183 /* Now, we wait for replies to come back and then continue with USERS,
184 CMODE and TOPIC commands. */
187 /* Received identify reply for a channel entry */
189 SILC_CLIENT_CMD_FUNC(resume_identify)
191 SilcClientResumeSession session = context;
192 SilcClientCommandReplyContext cmd = context2;
193 SilcClient client = session->client;
194 SilcClientConnection conn = session->conn;
197 SilcChannelEntry channel = NULL;
198 SilcChannelID *channel_id;
202 SILC_LOG_DEBUG(("Start"));
204 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
208 if (cmd->error != SILC_STATUS_OK) {
209 /* Delete unknown channel from our cache */
210 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
211 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
213 channel = silc_client_get_channel_by_id(client, conn, channel_id);
215 silc_client_del_channel(client, conn, channel);
216 silc_free(channel_id);
222 idp = silc_id_payload_parse(tmp, tmp_len);
226 id_type = silc_id_payload_get_type(idp);
229 case SILC_ID_CHANNEL:
230 channel_id = silc_id_payload_get_id(idp);
231 channel = silc_client_get_channel_by_id(client, conn, channel_id);
232 silc_free(channel_id);
235 silc_id_payload_free(idp);
240 /* Now, send CMODE command for this channel. We send only this one
241 because this will return also error if we are not currently joined
242 on this channel, plus we get the channel mode. USERS and TOPIC
243 commands are called after this returns. */
245 SILC_LOG_DEBUG(("Sending CMODE"));
246 silc_client_command_register(client, SILC_COMMAND_CMODE, NULL, NULL,
247 silc_client_command_reply_resume, 0,
249 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
250 conn->cmd_ident, 1, 1, tmp, tmp_len);
251 silc_client_command_pending(conn, SILC_COMMAND_CMODE, conn->cmd_ident,
252 silc_client_command_resume_cmode, session);
255 silc_id_payload_free(idp);
257 if (cmd->status != SILC_STATUS_OK &&
258 cmd->status != SILC_STATUS_LIST_END)
261 /* Unregister this command reply */
262 silc_client_command_unregister(client, SILC_COMMAND_IDENTIFY, NULL,
263 silc_client_command_reply_resume,
268 session->channel_count--;
269 if (!session->channel_count)
270 RESUME_CALL_COMPLETION(client, session, FALSE);
273 /* Received cmode to channel entry */
275 SILC_CLIENT_CMD_FUNC(resume_cmode)
277 SilcClientResumeSession session = context;
278 SilcClientCommandReplyContext cmd = context2;
279 SilcClient client = session->client;
280 SilcClientConnection conn = session->conn;
282 SilcChannelID *channel_id;
283 SilcChannelEntry channel;
286 SILC_LOG_DEBUG(("Start"));
288 /* Unregister this command reply */
289 silc_client_command_unregister(client, SILC_COMMAND_CMODE, NULL,
290 silc_client_command_reply_resume,
293 if (cmd->error != SILC_STATUS_OK)
296 /* Take Channel ID */
297 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
300 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
304 /* Get the channel entry */
305 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
308 /* Get channel mode */
309 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
311 SILC_GET32_MSB(channel->mode, tmp);
313 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
315 /* And now, we will send USERS to get users on the channel */
316 SILC_LOG_DEBUG(("Sending USERS"));
317 silc_client_command_register(client, SILC_COMMAND_USERS, NULL, NULL,
318 silc_client_command_reply_users_i, 0,
320 silc_client_command_send(client, conn, SILC_COMMAND_USERS,
321 conn->cmd_ident, 1, 1, tmp, len);
322 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
323 silc_client_command_resume_users, session);
326 silc_free(channel_id);
330 session->channel_count--;
331 if (!session->channel_count)
332 RESUME_CALL_COMPLETION(client, session, FALSE);
335 /* Received users reply to a channel entry */
337 SILC_CLIENT_CMD_FUNC(resume_users)
339 SilcClientResumeSession session = context;
340 SilcClientCommandReplyContext cmd = context2;
341 SilcClient client = session->client;
342 SilcClientConnection conn = session->conn;
343 SilcBufferStruct client_id_list, client_mode_list;
345 SilcUInt32 tmp_len, list_count;
346 SilcChannelEntry channel;
347 SilcChannelID *channel_id = NULL;
349 SILC_LOG_DEBUG(("Start"));
351 /* Unregister this command reply */
352 silc_client_command_unregister(client, SILC_COMMAND_USERS, NULL,
353 silc_client_command_reply_users_i,
356 if (cmd->error != SILC_STATUS_OK)
360 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
362 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
365 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
367 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
371 /* Get the list count */
372 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
374 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
377 SILC_GET32_MSB(list_count, tmp);
379 /* Get Client ID list */
380 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
382 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
385 silc_buffer_set(&client_id_list, tmp, tmp_len);
387 /* Get client mode list */
388 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
390 COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
393 silc_buffer_set(&client_mode_list, tmp, tmp_len);
395 /* Get channel entry */
396 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
400 /* Send fake JOIN command reply to application */
401 client->internal->ops->command_reply(client, conn, cmd->payload, TRUE,
402 SILC_COMMAND_JOIN, cmd->status,
403 channel->channel_name, channel,
405 NULL, NULL, NULL, NULL,
406 channel->hmac, list_count,
407 &client_id_list, client_mode_list);
409 /* Send TOPIC for this channel to get the topic */
410 SILC_LOG_DEBUG(("Sending TOPIC"));
411 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
412 silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
413 ++conn->cmd_ident, 1, 1, tmp, tmp_len);
415 /* Call the completion callback after we've got reply to all of
417 session->channel_count--;
418 if (!session->channel_count)
419 RESUME_CALL_COMPLETION(client, session, TRUE);
421 silc_free(channel_id);
425 silc_free(channel_id);
426 session->channel_count--;
427 if (!session->channel_count)
428 RESUME_CALL_COMPLETION(client, session, FALSE);