5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 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 * Command reply functions are "the otherside" of the command functions.
22 * Reply to a command sent by server is handled by these functions.
24 * The arguments received from server are also passed to the calling
25 * application through command_reply client operation. The arguments are
26 * exactly same and in same order as the server sent it. However, ID's are
27 * not sent to the application. Instead, corresponding ID entry is sent
28 * to the application. For example, instead of sending Client ID the
29 * corresponding SilcClientEntry is sent to the application. The case is
30 * same with for example Channel ID's. This way application has all the
31 * necessary data already in hand without redundant searching. If ID is
32 * received but ID entry does not exist, NULL is sent.
35 #include "silcincludes.h"
36 #include "silcclient.h"
37 #include "client_internal.h"
39 #define SAY cmd->client->internal->ops->say
41 /* All functions that call the COMMAND_CHECK_STATUS macro must have
42 out: and err: goto labels. out label should call the pending
43 command replies, and the err label just handle error condition. */
45 #define COMMAND_CHECK_STATUS \
47 SILC_LOG_DEBUG(("Start")); \
48 if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \
49 if (SILC_STATUS_IS_ERROR(cmd->status)) { \
51 COMMAND_REPLY_ERROR; \
54 /* List of errors */ \
55 COMMAND_REPLY_ERROR; \
56 if (cmd->status == SILC_STATUS_LIST_END) \
62 /* Same as COMMAND_CHECK_STATUS but doesn't call client operation */
63 #define COMMAND_CHECK_STATUS_I \
65 SILC_LOG_DEBUG(("Start")); \
66 if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \
67 if (SILC_STATUS_IS_ERROR(cmd->status)) \
69 if (cmd->status == SILC_STATUS_LIST_END) \
75 /* Process received command reply. */
77 void silc_client_command_reply_process(SilcClient client,
78 SilcSocketConnection sock,
79 SilcPacketContext *packet)
81 SilcBuffer buffer = packet->buffer;
82 SilcClientCommand cmd;
83 SilcClientCommandReplyContext ctx;
84 SilcCommandPayload payload;
86 SilcCommandCb reply = NULL;
88 /* Get command reply payload from packet */
89 payload = silc_command_payload_parse(buffer->data, buffer->len);
91 /* Silently ignore bad reply packet */
92 SILC_LOG_DEBUG(("Bad command reply packet"));
96 /* Allocate command reply context. This must be free'd by the
97 command reply routine receiving it. */
98 ctx = silc_calloc(1, sizeof(*ctx));
100 ctx->client = client;
102 ctx->payload = payload;
103 ctx->args = silc_command_get_args(ctx->payload);
104 ctx->packet = packet;
105 ctx->ident = silc_command_get_ident(ctx->payload);
106 silc_command_get_status(ctx->payload, &ctx->status, &ctx->error);
108 /* Check for pending commands and mark to be exeucted */
110 silc_client_command_pending_check(sock->user_data, ctx,
111 silc_command_get(ctx->payload),
112 ctx->ident, &ctx->callbacks_count);
114 /* Execute command reply */
116 command = silc_command_get(ctx->payload);
118 /* Try to find matching the command identifier */
119 silc_list_start(client->internal->commands);
120 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
121 if (cmd->cmd == command && !cmd->ident)
123 if (cmd->cmd == command && cmd->ident == ctx->ident) {
124 (*cmd->reply)((void *)ctx, NULL);
129 if (cmd == SILC_LIST_END) {
131 /* No specific identifier for command reply, call first one found */
138 /* Duplicate Command Reply Context by adding reference counter. The context
139 won't be free'd untill it hits zero. */
141 SilcClientCommandReplyContext
142 silc_client_command_reply_dup(SilcClientCommandReplyContext cmd)
145 SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd,
146 cmd->users - 1, cmd->users));
150 /* Free command reply context and its internals. */
152 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
155 SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd,
156 cmd->users + 1, cmd->users));
157 if (cmd->users < 1) {
158 silc_command_payload_free(cmd->payload);
164 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
168 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
169 SilcClientID *client_id;
170 SilcClientEntry client_entry = NULL;
172 unsigned char *id_data, *tmp;
173 char *nickname = NULL, *username = NULL;
174 char *realname = NULL;
175 SilcUInt32 idle = 0, mode = 0;
176 SilcBufferStruct channels, ch_user_modes;
177 bool has_channels = FALSE, has_user_modes = FALSE;
178 unsigned char *fingerprint;
179 SilcUInt32 fingerprint_len;
181 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
188 client_id = silc_id_payload_parse_id(id_data, len, NULL);
195 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
196 username = silc_argument_get_arg_type(cmd->args, 4, &len);
197 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
198 if (!nickname || !username || !realname) {
204 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
206 silc_buffer_set(&channels, tmp, len);
210 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
212 SILC_GET32_MSB(mode, tmp);
214 tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
216 SILC_GET32_MSB(idle, tmp);
218 fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
220 tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
222 silc_buffer_set(&ch_user_modes, tmp, len);
223 has_user_modes = TRUE;
226 /* Check if we have this client cached already. */
227 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
229 SILC_LOG_DEBUG(("Adding new client entry"));
231 silc_client_add_client(cmd->client, conn, nickname, username, realname,
234 silc_client_update_client(cmd->client, conn, client_entry,
235 nickname, username, realname, mode);
236 silc_free(client_id);
239 if (fingerprint && !client_entry->fingerprint) {
240 client_entry->fingerprint = silc_memdup(fingerprint, fingerprint_len);
241 client_entry->fingerprint_len = fingerprint_len;
244 /* Take Requested Attributes if set. */
245 tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
247 if (client_entry->attrs)
248 silc_attribute_payload_list_free(client_entry->attrs);
249 client_entry->attrs = silc_attribute_payload_parse(tmp, len);
252 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
254 /* Notify application */
255 if (!cmd->callbacks_count && notify)
256 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
257 has_channels ? &channels : NULL, mode, idle,
258 fingerprint, has_user_modes ? &ch_user_modes : NULL,
259 client_entry->attrs));
262 /* Received reply for WHOIS command. This maybe called several times
263 for one WHOIS command as server may reply with list of results. */
265 SILC_CLIENT_CMD_REPLY_FUNC(whois)
267 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
268 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
270 COMMAND_CHECK_STATUS;
272 /* Save WHOIS info */
273 silc_client_command_reply_whois_save(cmd, cmd->status, TRUE);
275 /* Pending callbacks are not executed if this was an list entry */
276 if (cmd->status != SILC_STATUS_OK &&
277 cmd->status != SILC_STATUS_LIST_END) {
278 silc_client_command_reply_free(cmd);
283 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
286 /* If we received notify for invalid ID we'll remove the ID if we
288 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
289 SilcClientEntry client_entry;
292 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
295 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
297 client_entry = silc_client_get_client_by_id(cmd->client, conn,
300 silc_client_del_client(cmd->client, conn, client_entry);
301 silc_free(client_id);
306 silc_client_command_reply_free(cmd);
309 /* Received reply for WHOWAS command. */
311 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
313 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
314 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
315 SilcClientID *client_id;
316 SilcClientEntry client_entry = NULL;
318 unsigned char *id_data;
319 char *nickname, *username;
320 char *realname = NULL;
322 COMMAND_CHECK_STATUS;
324 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
330 client_id = silc_id_payload_parse_id(id_data, len, NULL);
336 /* Get the client entry, if exists */
337 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
338 silc_free(client_id);
340 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
341 username = silc_argument_get_arg_type(cmd->args, 4, &len);
342 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
343 if (!nickname || !username) {
348 /* Notify application. We don't save any history information to any
349 cache. Just pass the data to the application for displaying on
351 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
353 /* Pending callbacks are not executed if this was an list entry */
354 if (cmd->status != SILC_STATUS_OK &&
355 cmd->status != SILC_STATUS_LIST_END) {
356 silc_client_command_reply_free(cmd);
361 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
363 silc_client_command_reply_free(cmd);
367 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
371 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
372 SilcClient client = cmd->client;
373 SilcClientID *client_id = NULL;
374 SilcServerID *server_id = NULL;
375 SilcChannelID *channel_id = NULL;
376 SilcClientEntry client_entry;
377 SilcServerEntry server_entry;
378 SilcChannelEntry channel_entry;
380 unsigned char *id_data;
381 char *name = NULL, *info = NULL;
382 SilcIDPayload idp = NULL;
385 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
391 idp = silc_id_payload_parse(id_data, len);
398 name = silc_argument_get_arg_type(cmd->args, 3, &len);
399 info = silc_argument_get_arg_type(cmd->args, 4, &len);
401 id_type = silc_id_payload_get_type(idp);
405 client_id = silc_id_payload_get_id(idp);
407 SILC_LOG_DEBUG(("Received client information"));
409 /* Check if we have this client cached already. */
410 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
412 SILC_LOG_DEBUG(("Adding new client entry"));
414 silc_client_add_client(cmd->client, conn, name, info, NULL,
415 silc_id_dup(client_id, id_type), 0);
417 silc_client_update_client(cmd->client, conn, client_entry,
418 name, info, NULL, 0);
421 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
423 /* Notify application */
425 COMMAND_REPLY((ARGS, client_entry, name, info));
429 server_id = silc_id_payload_get_id(idp);
431 SILC_LOG_DEBUG(("Received server information"));
433 /* Check if we have this server cached already. */
434 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
436 SILC_LOG_DEBUG(("Adding new server entry"));
437 server_entry = silc_client_add_server(cmd->client, conn, name, info,
438 silc_id_dup(server_id, id_type));
445 silc_client_update_server(client, conn, server_entry, name, info);
448 server_entry->resolve_cmd_ident = 0;
450 /* Notify application */
452 COMMAND_REPLY((ARGS, server_entry, name, info));
455 case SILC_ID_CHANNEL:
456 channel_id = silc_id_payload_get_id(idp);
458 SILC_LOG_DEBUG(("Received channel information"));
460 /* Check if we have this channel cached already. */
461 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
462 if (!channel_entry) {
466 /* Add new channel entry */
467 channel_entry = silc_client_add_channel(client, conn, name, 0,
472 /* Notify application */
474 COMMAND_REPLY((ARGS, channel_entry, name, info));
478 silc_id_payload_free(idp);
479 silc_free(client_id);
480 silc_free(server_id);
481 silc_free(channel_id);
484 /* Received reply for IDENTIFY command. This maybe called several times
485 for one IDENTIFY command as server may reply with list of results.
486 This is totally silent and does not print anything on screen. */
488 SILC_CLIENT_CMD_REPLY_FUNC(identify)
490 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
491 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
493 COMMAND_CHECK_STATUS;
495 /* Save IDENTIFY info */
496 silc_client_command_reply_identify_save(cmd, cmd->status, TRUE);
498 /* Pending callbacks are not executed if this was an list entry */
499 if (cmd->status != SILC_STATUS_OK &&
500 cmd->status != SILC_STATUS_LIST_END) {
501 silc_client_command_reply_free(cmd);
506 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
509 /* If we received notify for invalid ID we'll remove the ID if we
511 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
512 SilcClientEntry client_entry;
515 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
518 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
520 client_entry = silc_client_get_client_by_id(cmd->client, conn,
523 silc_client_del_client(cmd->client, conn, client_entry);
524 silc_free(client_id);
529 silc_client_command_reply_free(cmd);
532 /* Received reply for command NICK. If everything went without errors
533 we just received our new Client ID. */
535 SILC_CLIENT_CMD_REPLY_FUNC(nick)
537 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
538 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
541 SilcUInt32 argc, len;
543 SILC_LOG_DEBUG(("Start"));
545 if (cmd->error != SILC_STATUS_OK) {
546 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
547 "Cannot set nickname: %s",
548 silc_get_status_message(cmd->error));
553 argc = silc_argument_get_arg_num(cmd->args);
554 if (argc < 2 || argc > 3) {
555 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
556 "Cannot set nickname: bad reply to command");
561 /* Take received Client ID */
562 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
563 idp = silc_id_payload_parse(tmp, len);
568 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
570 /* Take the new nickname too */
571 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
573 silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
575 silc_free(conn->nickname);
576 conn->nickname = strdup(tmp);
577 conn->local_entry->nickname = conn->nickname;
578 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
579 silc_idcache_add(conn->client_cache, strdup(tmp),
580 conn->local_entry->id, conn->local_entry, 0, NULL);
583 /* Notify application */
584 COMMAND_REPLY((ARGS, conn->local_entry, conn->local_entry->nickname));
587 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
588 silc_client_command_reply_free(cmd);
591 /* Received reply to the LIST command. */
593 SILC_CLIENT_CMD_REPLY_FUNC(list)
595 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
596 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
597 unsigned char *tmp, *name, *topic;
598 SilcUInt32 usercount = 0, len;
599 SilcChannelID *channel_id = NULL;
600 SilcChannelEntry channel_entry;
602 COMMAND_CHECK_STATUS;
604 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
610 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
616 name = silc_argument_get_arg_type(cmd->args, 3, NULL);
622 topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
623 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
625 SILC_GET32_MSB(usercount, tmp);
627 /* Check whether the channel exists, and add it to cache if it doesn't. */
628 channel_entry = silc_client_get_channel_by_id(cmd->client, conn,
630 if (!channel_entry) {
631 /* Add new channel entry */
632 channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
634 if (!channel_entry) {
641 /* Notify application */
642 COMMAND_REPLY((ARGS, channel_entry, name, topic, usercount));
644 /* Pending callbacks are not executed if this was an list entry */
645 if (cmd->status != SILC_STATUS_OK &&
646 cmd->status != SILC_STATUS_LIST_END) {
647 silc_client_command_reply_free(cmd);
652 silc_free(channel_id);
653 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
655 silc_client_command_reply_free(cmd);
658 /* Received reply to topic command. */
660 SILC_CLIENT_CMD_REPLY_FUNC(topic)
662 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
663 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
664 SilcChannelEntry channel;
665 SilcChannelID *channel_id = NULL;
668 SilcUInt32 argc, len;
670 if (cmd->error != SILC_STATUS_OK) {
671 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
672 "%s", silc_get_status_message(cmd->error));
677 argc = silc_argument_get_arg_num(cmd->args);
678 if (argc < 1 || argc > 3) {
683 /* Take Channel ID */
684 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
689 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
693 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
697 /* Get the channel entry */
698 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
700 silc_free(channel_id);
705 /* Notify application */
706 COMMAND_REPLY((ARGS, channel, topic));
709 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
710 silc_client_command_reply_free(cmd);
713 /* Received reply to invite command. */
715 SILC_CLIENT_CMD_REPLY_FUNC(invite)
717 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
718 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
719 SilcChannelEntry channel;
720 SilcChannelID *channel_id;
724 if (cmd->error != SILC_STATUS_OK) {
725 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
726 "%s", silc_get_status_message(cmd->error));
731 /* Take Channel ID */
732 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
736 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
740 /* Get the channel entry */
741 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
743 silc_free(channel_id);
748 /* Get the invite list */
749 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
751 /* Notify application */
752 COMMAND_REPLY((ARGS, channel, tmp));
755 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
756 silc_client_command_reply_free(cmd);
759 /* Received reply to the KILL command. */
761 SILC_CLIENT_CMD_REPLY_FUNC(kill)
763 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
764 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
766 if (cmd->error != SILC_STATUS_OK) {
767 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
768 "%s", silc_get_status_message(cmd->error));
773 /* Notify application */
774 COMMAND_REPLY((ARGS));
777 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
778 silc_client_command_reply_free(cmd);
781 /* Received reply to INFO command. We receive the server ID and some
782 information about the server user requested. */
784 SILC_CLIENT_CMD_REPLY_FUNC(info)
786 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
787 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
789 SilcServerEntry server;
790 SilcServerID *server_id = NULL;
791 char *server_name, *server_info;
794 SILC_LOG_DEBUG(("Start"));
796 if (cmd->error != SILC_STATUS_OK) {
797 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
798 silc_get_status_message(cmd->error));
804 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
808 server_id = silc_id_payload_parse_id(tmp, len, NULL);
812 /* Get server name */
813 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
817 /* Get server info */
818 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
822 /* See whether we have this server cached. If not create it. */
823 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
825 SILC_LOG_DEBUG(("New server entry"));
826 server = silc_client_add_server(cmd->client, conn, server_name,
828 silc_id_dup(server_id, SILC_ID_SERVER));
833 /* Notify application */
834 COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
837 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
838 silc_free(server_id);
839 silc_client_command_reply_free(cmd);
842 /* Received reply to STATS command. */
844 SILC_CLIENT_CMD_REPLY_FUNC(stats)
846 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
847 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
848 unsigned char *tmp, *buf = NULL;
849 SilcUInt32 len, buf_len = 0;
851 if (cmd->error != SILC_STATUS_OK) {
852 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
853 "%s", silc_get_status_message(cmd->error));
859 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
863 /* Get statistics structure */
864 buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
866 /* Notify application */
867 COMMAND_REPLY((ARGS, buf, buf_len));
870 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
871 silc_client_command_reply_free(cmd);
874 /* Received reply to PING command. The reply time is shown to user. */
876 SILC_CLIENT_CMD_REPLY_FUNC(ping)
878 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
879 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
882 time_t diff, curtime;
884 if (cmd->error != SILC_STATUS_OK) {
885 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
886 "%s", silc_get_status_message(cmd->error));
891 curtime = time(NULL);
892 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
893 cmd->packet->src_id_type);
894 if (!id || !conn->ping) {
899 for (i = 0; i < conn->ping_count; i++) {
900 if (!conn->ping[i].dest_id)
902 if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
903 diff = curtime - conn->ping[i].start_time;
904 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
905 "Ping reply from %s: %d second%s",
906 conn->ping[i].dest_name, diff,
907 diff == 1 ? "" : "s");
909 conn->ping[i].start_time = 0;
910 silc_free(conn->ping[i].dest_id);
911 conn->ping[i].dest_id = NULL;
912 silc_free(conn->ping[i].dest_name);
913 conn->ping[i].dest_name = NULL;
920 /* Notify application */
921 COMMAND_REPLY((ARGS));
924 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
925 silc_client_command_reply_free(cmd);
928 /* Received reply for JOIN command. */
930 SILC_CLIENT_CMD_REPLY_FUNC(join)
932 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
933 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
934 SilcChannelEntry channel;
936 SilcChannelID *channel_id;
937 SilcUInt32 argc, mode = 0, len, list_count;
938 char *topic, *tmp, *channel_name = NULL, *hmac;
939 SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
942 SILC_LOG_DEBUG(("Start"));
944 if (cmd->error != SILC_STATUS_OK) {
945 if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
946 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
947 "%s", silc_get_status_message(cmd->error));
952 argc = silc_argument_get_arg_num(cmd->args);
954 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
955 "Cannot join channel: Bad reply packet");
960 /* Get channel name */
961 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
963 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
964 "Cannot join channel: Bad reply packet");
971 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
973 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
974 "Cannot join channel: Bad reply packet");
978 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
984 /* Get channel mode */
985 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
987 SILC_GET32_MSB(mode, tmp);
989 /* Get channel key */
990 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
992 keyp = silc_buffer_alloc(len);
993 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
994 silc_buffer_put(keyp, tmp, len);
998 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1000 /* Check whether we have this channel entry already. */
1001 channel = silc_client_get_channel(cmd->client, conn, channel_name);
1003 if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1004 silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1006 /* Create new channel entry */
1007 channel = silc_client_add_channel(cmd->client, conn, channel_name,
1011 conn->current_channel = channel;
1014 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1016 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1017 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1018 "Cannot join channel: Unsupported HMAC `%s'", hmac);
1019 COMMAND_REPLY_ERROR;
1024 /* Get the list count */
1025 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1028 SILC_GET32_MSB(list_count, tmp);
1030 /* Get Client ID list */
1031 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1035 client_id_list = silc_buffer_alloc(len);
1036 silc_buffer_pull_tail(client_id_list, len);
1037 silc_buffer_put(client_id_list, tmp, len);
1039 /* Get client mode list */
1040 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1044 client_mode_list = silc_buffer_alloc(len);
1045 silc_buffer_pull_tail(client_mode_list, len);
1046 silc_buffer_put(client_mode_list, tmp, len);
1048 /* Add clients we received in the reply to the channel */
1049 for (i = 0; i < list_count; i++) {
1052 SilcClientID *client_id;
1053 SilcClientEntry client_entry;
1056 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1058 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1063 SILC_GET32_MSB(mode, client_mode_list->data);
1065 /* Check if we have this client cached already. */
1066 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1067 if (!client_entry) {
1068 /* No, we don't have it, add entry for it. */
1070 silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1071 silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1074 /* Join client to the channel */
1075 if (!silc_client_on_channel(channel, client_entry)) {
1076 chu = silc_calloc(1, sizeof(*chu));
1077 chu->client = client_entry;
1078 chu->channel = channel;
1080 silc_hash_table_add(channel->user_list, client_entry, chu);
1081 silc_hash_table_add(client_entry->channels, channel, chu);
1084 silc_free(client_id);
1085 silc_buffer_pull(client_id_list, idp_len);
1086 silc_buffer_pull(client_mode_list, 4);
1088 silc_buffer_push(client_id_list, client_id_list->data -
1089 client_id_list->head);
1090 silc_buffer_push(client_mode_list, client_mode_list->data -
1091 client_mode_list->head);
1093 /* Save channel key */
1094 if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1095 silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1097 /* Notify application */
1098 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0,
1099 keyp ? keyp->head : NULL, NULL,
1100 NULL, topic, hmac, list_count, client_id_list,
1104 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1105 silc_client_command_reply_free(cmd);
1108 silc_buffer_free(keyp);
1110 silc_buffer_free(client_id_list);
1111 if (client_mode_list)
1112 silc_buffer_free(client_mode_list);
1115 /* Received reply for MOTD command */
1117 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1119 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1120 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1122 char *motd = NULL, *cp, line[256];
1124 if (cmd->error != SILC_STATUS_OK) {
1125 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1126 "%s", silc_get_status_message(cmd->error));
1127 COMMAND_REPLY_ERROR;
1131 argc = silc_argument_get_arg_num(cmd->args);
1133 COMMAND_REPLY_ERROR;
1138 motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1140 COMMAND_REPLY_ERROR;
1147 if (cp[i++] == '\n') {
1148 memset(line, 0, sizeof(line));
1149 silc_strncat(line, sizeof(line), cp, i - 1);
1155 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1164 /* Notify application */
1165 COMMAND_REPLY((ARGS, motd));
1168 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1169 silc_client_command_reply_free(cmd);
1172 /* Received reply tot he UMODE command. Save the current user mode */
1174 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1176 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1177 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1181 if (cmd->error != SILC_STATUS_OK) {
1182 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1183 "%s", silc_get_status_message(cmd->error));
1184 COMMAND_REPLY_ERROR;
1188 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1190 COMMAND_REPLY_ERROR;
1194 SILC_GET32_MSB(mode, tmp);
1195 conn->local_entry->mode = mode;
1197 /* Notify application */
1198 COMMAND_REPLY((ARGS, mode));
1201 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1202 silc_client_command_reply_free(cmd);
1205 /* Received reply for CMODE command. */
1207 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1209 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1210 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1213 SilcChannelID *channel_id;
1214 SilcChannelEntry channel;
1217 if (cmd->error != SILC_STATUS_OK) {
1218 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1219 "%s", silc_get_status_message(cmd->error));
1220 COMMAND_REPLY_ERROR;
1224 /* Take Channel ID */
1225 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1228 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1232 /* Get the channel entry */
1233 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1235 silc_free(channel_id);
1236 COMMAND_REPLY_ERROR;
1240 /* Get channel mode */
1241 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1243 silc_free(channel_id);
1244 COMMAND_REPLY_ERROR;
1249 SILC_GET32_MSB(mode, tmp);
1250 channel->mode = mode;
1252 /* Notify application */
1253 COMMAND_REPLY((ARGS, channel, mode));
1255 silc_free(channel_id);
1258 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1259 silc_client_command_reply_free(cmd);
1262 /* Received reply for CUMODE command */
1264 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1266 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1267 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1268 SilcClientID *client_id;
1269 SilcChannelID *channel_id;
1270 SilcClientEntry client_entry;
1271 SilcChannelEntry channel;
1272 SilcChannelUser chu;
1273 unsigned char *modev, *tmp, *id;
1274 SilcUInt32 len, mode;
1276 if (cmd->error != SILC_STATUS_OK) {
1277 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1278 "%s", silc_get_status_message(cmd->error));
1279 COMMAND_REPLY_ERROR;
1283 /* Get channel mode */
1284 modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1286 COMMAND_REPLY_ERROR;
1290 /* Take Channel ID */
1291 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1294 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1298 /* Get the channel entry */
1299 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1301 silc_free(channel_id);
1302 COMMAND_REPLY_ERROR;
1307 id = silc_argument_get_arg_type(cmd->args, 4, &len);
1309 silc_free(channel_id);
1310 COMMAND_REPLY_ERROR;
1313 client_id = silc_id_payload_parse_id(id, len, NULL);
1315 silc_free(channel_id);
1316 COMMAND_REPLY_ERROR;
1320 /* Get client entry */
1321 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1322 if (!client_entry) {
1323 silc_free(channel_id);
1324 silc_free(client_id);
1325 COMMAND_REPLY_ERROR;
1330 SILC_GET32_MSB(mode, modev);
1331 chu = silc_client_on_channel(channel, client_entry);
1335 /* Notify application */
1336 COMMAND_REPLY((ARGS, mode, channel, client_entry));
1337 silc_free(client_id);
1338 silc_free(channel_id);
1341 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1342 silc_client_command_reply_free(cmd);
1345 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1347 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1348 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1350 if (cmd->error != SILC_STATUS_OK) {
1351 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1352 "%s", silc_get_status_message(cmd->error));
1353 COMMAND_REPLY_ERROR;
1357 /* Notify application */
1358 COMMAND_REPLY((ARGS));
1361 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1362 silc_client_command_reply_free(cmd);
1365 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1367 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1368 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1370 if (cmd->error != SILC_STATUS_OK) {
1371 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1372 "%s", silc_get_status_message(cmd->error));
1373 COMMAND_REPLY_ERROR;
1377 /* Notify application */
1378 COMMAND_REPLY((ARGS));
1381 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1382 silc_client_command_reply_free(cmd);
1385 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1387 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1388 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1390 if (cmd->error != SILC_STATUS_OK) {
1391 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1392 "%s", silc_get_status_message(cmd->error));
1393 COMMAND_REPLY_ERROR;
1397 /* Notify application */
1398 COMMAND_REPLY((ARGS));
1401 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1402 silc_client_command_reply_free(cmd);
1405 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1407 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1408 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1411 if (cmd->error != SILC_STATUS_OK) {
1412 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1413 "%s", silc_get_status_message(cmd->error));
1414 COMMAND_REPLY_ERROR;
1418 /* Notify application */
1419 COMMAND_REPLY((ARGS));
1421 /* Generate the detachment data and deliver it to the client in the
1422 detach client operation */
1423 detach = silc_client_get_detach_data(cmd->client, conn);
1425 cmd->client->internal->ops->detach(cmd->client, conn,
1426 detach->data, detach->len);
1427 silc_buffer_free(detach);
1431 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1432 silc_client_command_reply_free(cmd);
1435 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1437 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1438 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1440 if (cmd->error != SILC_STATUS_OK) {
1441 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1442 "%s", silc_get_status_message(cmd->error));
1443 COMMAND_REPLY_ERROR;
1447 /* Notify application */
1448 COMMAND_REPLY((ARGS));
1451 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1452 silc_client_command_reply_free(cmd);
1455 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1457 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1458 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1459 SilcChannelEntry channel;
1460 SilcChannelID *channel_id;
1464 if (cmd->error != SILC_STATUS_OK) {
1465 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1466 "%s", silc_get_status_message(cmd->error));
1467 COMMAND_REPLY_ERROR;
1471 /* Take Channel ID */
1472 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1476 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1480 /* Get the channel entry */
1481 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1483 silc_free(channel_id);
1484 COMMAND_REPLY_ERROR;
1488 /* Get the ban list */
1489 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1491 /* Notify application */
1492 COMMAND_REPLY((ARGS, channel, tmp));
1495 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1496 silc_client_command_reply_free(cmd);
1499 /* Reply to LEAVE command. */
1501 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1503 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1504 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1505 SilcChannelID *channel_id;
1506 SilcChannelEntry channel = NULL;
1510 if (cmd->error != SILC_STATUS_OK) {
1511 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1512 "%s", silc_get_status_message(cmd->error));
1513 COMMAND_REPLY_ERROR;
1517 /* From protocol version 1.1 we get the channel ID of the left channel */
1518 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1520 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1524 /* Get the channel entry */
1525 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1527 silc_free(channel_id);
1528 COMMAND_REPLY_ERROR;
1532 silc_free(channel_id);
1535 /* Notify application */
1536 COMMAND_REPLY((ARGS, channel));
1539 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1540 silc_client_command_reply_free(cmd);
1543 /* Channel resolving callback for USERS command reply. */
1545 static void silc_client_command_reply_users_cb(SilcClient client,
1546 SilcClientConnection conn,
1547 SilcChannelEntry *channels,
1548 SilcUInt32 channels_count,
1551 if (!channels_count) {
1552 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1553 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1555 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1556 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1557 "%s", silc_get_status_message(cmd->error));
1558 COMMAND_REPLY_ERROR;
1559 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1560 silc_client_command_reply_free(cmd);
1564 silc_client_command_reply_users(context, NULL);
1568 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1571 SilcGetChannelCallback get_channel,
1572 SilcCommandCb get_clients)
1574 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1575 SilcChannelEntry channel;
1576 SilcClientEntry client_entry;
1577 SilcChannelUser chu;
1578 SilcChannelID *channel_id = NULL;
1579 SilcBufferStruct client_id_list, client_mode_list;
1581 SilcUInt32 tmp_len, list_count;
1583 unsigned char **res_argv = NULL;
1584 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1585 bool wait_res = FALSE;
1587 SILC_LOG_DEBUG(("Start"));
1589 /* Get channel ID */
1590 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1592 COMMAND_REPLY_ERROR;
1595 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1597 COMMAND_REPLY_ERROR;
1601 /* Get the list count */
1602 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1604 COMMAND_REPLY_ERROR;
1607 SILC_GET32_MSB(list_count, tmp);
1609 /* Get Client ID list */
1610 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1612 COMMAND_REPLY_ERROR;
1615 silc_buffer_set(&client_id_list, tmp, tmp_len);
1617 /* Get client mode list */
1618 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1620 COMMAND_REPLY_ERROR;
1623 silc_buffer_set(&client_mode_list, tmp, tmp_len);
1625 /* Get channel entry */
1626 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1628 /* Resolve the channel from server */
1629 silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1631 silc_free(channel_id);
1635 SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1637 /* Cache the received Client ID's and modes. */
1638 for (i = 0; i < list_count; i++) {
1641 SilcClientID *client_id;
1644 SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1646 client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1651 SILC_GET32_MSB(mode, client_mode_list.data);
1653 /* Check if we have this client cached already. */
1654 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1655 if (!client_entry || !client_entry->username || !client_entry->realname) {
1656 /* No we don't have it (or it is incomplete in information), query
1657 it from the server. Assemble argument table that will be sent
1658 for the WHOIS command later. */
1659 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1661 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1663 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1665 res_argv[res_argc] = client_id_list.data;
1666 res_argv_lens[res_argc] = idp_len;
1667 res_argv_types[res_argc] = res_argc + 4;
1670 if (!silc_client_on_channel(channel, client_entry)) {
1671 chu = silc_calloc(1, sizeof(*chu));
1672 chu->client = client_entry;
1674 chu->channel = channel;
1675 silc_hash_table_add(channel->user_list, client_entry, chu);
1676 silc_hash_table_add(client_entry->channels, channel, chu);
1680 silc_free(client_id);
1681 silc_buffer_pull(&client_id_list, idp_len);
1682 silc_buffer_pull(&client_mode_list, 4);
1685 /* Query the client information from server if the list included clients
1686 that we don't know about. */
1690 /* Send the WHOIS command to server */
1691 silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1692 silc_client_command_reply_whois_i, 0,
1694 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1695 res_argc, res_argv, res_argv_lens,
1696 res_argv_types, conn->cmd_ident);
1697 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1698 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1701 /* Register pending command callback. After we've received the WHOIS
1702 command reply we will reprocess this command reply by re-calling this
1703 USERS command reply callback. */
1704 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1707 silc_buffer_free(res_cmd);
1708 silc_free(channel_id);
1709 silc_free(res_argv);
1710 silc_free(res_argv_lens);
1711 silc_free(res_argv_types);
1718 silc_buffer_push(&client_id_list, (client_id_list.data -
1719 client_id_list.head));
1720 silc_buffer_push(&client_mode_list, (client_mode_list.data -
1721 client_mode_list.head));
1723 /* Notify application */
1725 COMMAND_REPLY((ARGS, channel, list_count, &client_id_list,
1726 &client_mode_list));
1729 silc_free(channel_id);
1733 /* Reply to USERS command. Received list of client ID's and theirs modes
1734 on the channel we requested. */
1736 SILC_CLIENT_CMD_REPLY_FUNC(users)
1738 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1739 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1741 SILC_LOG_DEBUG(("Start"));
1743 if (cmd->error != SILC_STATUS_OK) {
1744 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1745 "%s", silc_get_status_message(cmd->error));
1746 COMMAND_REPLY_ERROR;
1750 if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE,
1751 silc_client_command_reply_users_cb,
1752 silc_client_command_reply_users))
1756 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1757 silc_client_command_reply_free(cmd);
1760 /* Received command reply to GETKEY command. WE've received the remote
1761 client's public key. */
1763 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1765 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1766 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1767 SilcIDPayload idp = NULL;
1768 SilcClientID *client_id = NULL;
1769 SilcClientEntry client_entry;
1770 SilcServerID *server_id = NULL;
1771 SilcServerEntry server_entry;
1773 unsigned char *tmp, *pk;
1777 SilcPublicKey public_key = NULL;
1779 SILC_LOG_DEBUG(("Start"));
1781 if (cmd->error != SILC_STATUS_OK) {
1782 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1783 "%s", silc_get_status_message(cmd->error));
1784 COMMAND_REPLY_ERROR;
1788 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1790 COMMAND_REPLY_ERROR;
1793 idp = silc_id_payload_parse(tmp, len);
1795 COMMAND_REPLY_ERROR;
1799 /* Get the public key payload */
1800 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1802 /* Decode the public key */
1803 SILC_GET16_MSB(pk_len, tmp);
1804 SILC_GET16_MSB(type, tmp + 2);
1807 if (type == SILC_SKE_PK_TYPE_SILC)
1808 if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1812 id_type = silc_id_payload_get_type(idp);
1813 if (id_type == SILC_ID_CLIENT) {
1814 /* Received client's public key */
1815 client_id = silc_id_payload_get_id(idp);
1816 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1817 if (!client_entry) {
1818 COMMAND_REPLY_ERROR;
1822 /* Notify application */
1823 COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1824 } else if (id_type == SILC_ID_SERVER) {
1825 /* Received server's public key */
1826 server_id = silc_id_payload_get_id(idp);
1827 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1828 if (!server_entry) {
1829 COMMAND_REPLY_ERROR;
1833 /* Notify application */
1834 COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1838 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1840 silc_id_payload_free(idp);
1842 silc_pkcs_public_key_free(public_key);
1843 silc_free(client_id);
1844 silc_free(server_id);
1845 silc_client_command_reply_free(cmd);
1848 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1850 silc_client_command_reply_free(context);
1854 /******************************************************************************
1856 Internal command reply functions
1858 ******************************************************************************/
1860 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1862 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1863 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1865 COMMAND_CHECK_STATUS_I;
1867 /* Save WHOIS info */
1868 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1870 /* Pending callbacks are not executed if this was an list entry */
1871 if (cmd->status != SILC_STATUS_OK &&
1872 cmd->status != SILC_STATUS_LIST_END) {
1873 silc_client_command_reply_free(cmd);
1878 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1881 /* If we received notify for invalid ID we'll remove the ID if we
1883 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1884 SilcClientEntry client_entry;
1886 unsigned char *tmp =
1887 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1890 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1892 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1895 silc_client_del_client(cmd->client, conn, client_entry);
1896 silc_free(client_id);
1901 /* Unregister this command reply */
1902 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1903 NULL, silc_client_command_reply_whois_i,
1906 silc_client_command_reply_free(cmd);
1909 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1911 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1912 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1914 COMMAND_CHECK_STATUS_I;
1916 /* Save IDENTIFY info */
1917 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1919 /* Pending callbacks are not executed if this was an list entry */
1920 if (cmd->status != SILC_STATUS_OK &&
1921 cmd->status != SILC_STATUS_LIST_END) {
1922 silc_client_command_reply_free(cmd);
1927 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1930 /* If we received notify for invalid ID we'll remove the ID if we
1932 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1933 SilcClientEntry client_entry;
1935 unsigned char *tmp =
1936 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1939 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1941 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1944 silc_client_del_client(cmd->client, conn, client_entry);
1945 silc_free(client_id);
1950 /* Unregister this command reply */
1951 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1952 NULL, silc_client_command_reply_identify_i,
1955 silc_client_command_reply_free(cmd);
1958 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
1960 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1961 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1963 SilcServerEntry server;
1964 SilcServerID *server_id = NULL;
1965 char *server_name, *server_info;
1968 COMMAND_CHECK_STATUS_I;
1971 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1975 server_id = silc_id_payload_parse_id(tmp, len, NULL);
1979 /* Get server name */
1980 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1984 /* Get server info */
1985 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
1989 /* See whether we have this server cached. If not create it. */
1990 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
1992 SILC_LOG_DEBUG(("New server entry"));
1993 silc_client_add_server(cmd->client, conn, server_name, server_info,
1994 silc_id_dup(server_id, SILC_ID_SERVER));
1998 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
1999 silc_free(server_id);
2001 silc_client_command_reply_free(cmd);
2004 static void silc_client_command_reply_users_i_cb(SilcClient client,
2005 SilcClientConnection conn,
2006 SilcChannelEntry *channels,
2007 SilcUInt32 channels_count,
2010 if (!channels_count) {
2011 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2012 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2014 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2015 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2016 "%s", silc_get_status_message(cmd->error));
2017 COMMAND_REPLY_ERROR;
2018 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2019 silc_client_command_reply_free(cmd);
2023 silc_client_command_reply_users_i(context, NULL);
2026 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2028 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2030 COMMAND_CHECK_STATUS_I;
2032 /* Save USERS info */
2033 if (silc_client_command_reply_users_save(
2034 cmd, cmd->status, FALSE,
2035 silc_client_command_reply_users_i_cb,
2036 silc_client_command_reply_users_i))
2040 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2043 /* Unregister this command reply */
2044 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2045 NULL, silc_client_command_reply_users_i,
2048 silc_client_command_reply_free(cmd);
2051 /* Private range commands, specific to this implementation (and compatible
2052 with SILC Server >= 0.9). */
2054 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2056 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2057 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2059 if (cmd->error != SILC_STATUS_OK) {
2060 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2061 "%s", silc_get_status_message(cmd->error));
2062 COMMAND_REPLY_ERROR;
2066 /* Notify application */
2067 COMMAND_REPLY((ARGS));
2070 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2071 silc_client_command_reply_free(cmd);
2074 SILC_CLIENT_CMD_REPLY_FUNC(close)
2076 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2077 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2079 if (cmd->error != SILC_STATUS_OK) {
2080 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2081 "%s", silc_get_status_message(cmd->error));
2082 COMMAND_REPLY_ERROR;
2086 /* Notify application */
2087 COMMAND_REPLY((ARGS));
2090 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2091 silc_client_command_reply_free(cmd);
2094 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2096 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2097 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2099 if (cmd->error != SILC_STATUS_OK) {
2100 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2101 "%s", silc_get_status_message(cmd->error));
2102 COMMAND_REPLY_ERROR;
2106 /* Notify application */
2107 COMMAND_REPLY((ARGS));
2110 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2111 silc_client_command_reply_free(cmd);