5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 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 * 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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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;
542 SilcClientID old_client_id;
544 SILC_LOG_DEBUG(("Start"));
546 if (cmd->error != SILC_STATUS_OK) {
547 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
548 "Cannot set nickname: %s",
549 silc_get_status_message(cmd->error));
554 argc = silc_argument_get_arg_num(cmd->args);
555 if (argc < 2 || argc > 3) {
556 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
557 "Cannot set nickname: bad reply to command");
562 /* Save old Client ID */
563 old_client_id = *conn->local_id;
565 /* Take received Client ID */
566 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
567 idp = silc_id_payload_parse(tmp, len);
572 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
574 /* Take the new nickname too */
575 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
577 silc_idcache_del_by_context(conn->internal->client_cache,
580 silc_free(conn->nickname);
581 conn->nickname = strdup(tmp);
582 conn->local_entry->nickname = conn->nickname;
583 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
584 silc_idcache_add(conn->internal->client_cache, strdup(tmp),
585 conn->local_entry->id, conn->local_entry, 0, NULL);
588 /* Notify application */
589 COMMAND_REPLY((SILC_ARGS, conn->local_entry, conn->local_entry->nickname,
590 (const SilcClientID *)&old_client_id));
593 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
594 silc_client_command_reply_free(cmd);
597 /* Received reply to the LIST command. */
599 SILC_CLIENT_CMD_REPLY_FUNC(list)
601 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
602 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
603 unsigned char *tmp, *name, *topic;
604 SilcUInt32 usercount = 0, len;
605 SilcChannelID *channel_id = NULL;
606 SilcChannelEntry channel_entry;
608 COMMAND_CHECK_STATUS;
610 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
616 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
622 name = silc_argument_get_arg_type(cmd->args, 3, NULL);
628 topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
629 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
631 SILC_GET32_MSB(usercount, tmp);
633 /* Check whether the channel exists, and add it to cache if it doesn't. */
634 channel_entry = silc_client_get_channel_by_id(cmd->client, conn,
636 if (!channel_entry) {
637 /* Add new channel entry */
638 channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
640 if (!channel_entry) {
647 /* Notify application */
648 COMMAND_REPLY((SILC_ARGS, channel_entry, name, topic, usercount));
650 /* Pending callbacks are not executed if this was an list entry */
651 if (cmd->status != SILC_STATUS_OK &&
652 cmd->status != SILC_STATUS_LIST_END) {
653 silc_client_command_reply_free(cmd);
658 silc_free(channel_id);
659 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
661 silc_client_command_reply_free(cmd);
664 /* Received reply to topic command. */
666 SILC_CLIENT_CMD_REPLY_FUNC(topic)
668 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
669 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
670 SilcChannelEntry channel;
671 SilcChannelID *channel_id = NULL;
674 SilcUInt32 argc, len;
676 if (cmd->error != SILC_STATUS_OK) {
677 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
678 "%s", silc_get_status_message(cmd->error));
683 argc = silc_argument_get_arg_num(cmd->args);
684 if (argc < 1 || argc > 3) {
689 /* Take Channel ID */
690 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
695 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
699 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
703 /* Get the channel entry */
704 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
706 silc_free(channel_id);
711 /* Notify application */
712 COMMAND_REPLY((SILC_ARGS, channel, topic));
715 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
716 silc_client_command_reply_free(cmd);
719 /* Received reply to invite command. */
721 SILC_CLIENT_CMD_REPLY_FUNC(invite)
723 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
724 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
725 SilcChannelEntry channel;
726 SilcChannelID *channel_id;
729 SilcBufferStruct buf;
731 if (cmd->error != SILC_STATUS_OK) {
732 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
733 "%s", silc_get_status_message(cmd->error));
738 /* Take Channel ID */
739 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
743 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
747 /* Get the channel entry */
748 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
750 silc_free(channel_id);
755 /* Get the invite list */
756 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
758 silc_buffer_set(&buf, tmp, len);
760 /* Notify application */
761 COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
764 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
765 silc_client_command_reply_free(cmd);
768 /* Received reply to the KILL command. */
770 SILC_CLIENT_CMD_REPLY_FUNC(kill)
772 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
773 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
775 if (cmd->error != SILC_STATUS_OK) {
776 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
777 "%s", silc_get_status_message(cmd->error));
782 /* Notify application */
783 COMMAND_REPLY((SILC_ARGS));
786 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
787 silc_client_command_reply_free(cmd);
790 /* Received reply to INFO command. We receive the server ID and some
791 information about the server user requested. */
793 SILC_CLIENT_CMD_REPLY_FUNC(info)
795 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
796 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
798 SilcServerEntry server;
799 SilcServerID *server_id = NULL;
800 char *server_name, *server_info;
803 SILC_LOG_DEBUG(("Start"));
805 if (cmd->error != SILC_STATUS_OK) {
806 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
807 silc_get_status_message(cmd->error));
813 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
817 server_id = silc_id_payload_parse_id(tmp, len, NULL);
821 /* Get server name */
822 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
826 /* Get server info */
827 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
831 /* See whether we have this server cached. If not create it. */
832 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
834 SILC_LOG_DEBUG(("New server entry"));
835 server = silc_client_add_server(cmd->client, conn, server_name,
837 silc_id_dup(server_id, SILC_ID_SERVER));
842 /* Notify application */
843 COMMAND_REPLY((SILC_ARGS, server, server->server_name, server->server_info));
846 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
847 silc_free(server_id);
848 silc_client_command_reply_free(cmd);
851 /* Received reply to STATS command. */
853 SILC_CLIENT_CMD_REPLY_FUNC(stats)
855 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
856 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
857 unsigned char *tmp, *buf = NULL;
858 SilcUInt32 len, buf_len = 0;
860 if (cmd->error != SILC_STATUS_OK) {
861 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
862 "%s", silc_get_status_message(cmd->error));
868 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
872 /* Get statistics structure */
873 buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
875 /* Notify application */
876 COMMAND_REPLY((SILC_ARGS, buf, buf_len));
879 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
880 silc_client_command_reply_free(cmd);
883 /* Received reply to PING command. The reply time is shown to user. */
885 SILC_CLIENT_CMD_REPLY_FUNC(ping)
887 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
888 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
891 time_t diff, curtime;
893 if (cmd->error != SILC_STATUS_OK) {
894 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
895 "%s", silc_get_status_message(cmd->error));
900 curtime = time(NULL);
901 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
902 cmd->packet->src_id_type);
903 if (!id || !conn->internal->ping) {
908 for (i = 0; i < conn->internal->ping_count; i++) {
909 if (!conn->internal->ping[i].dest_id)
911 if (SILC_ID_SERVER_COMPARE(conn->internal->ping[i].dest_id, id)) {
912 diff = curtime - conn->internal->ping[i].start_time;
913 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
914 "Ping reply from %s: %d second%s",
915 conn->internal->ping[i].dest_name, diff,
916 diff == 1 ? "" : "s");
918 conn->internal->ping[i].start_time = 0;
919 silc_free(conn->internal->ping[i].dest_id);
920 conn->internal->ping[i].dest_id = NULL;
921 silc_free(conn->internal->ping[i].dest_name);
922 conn->internal->ping[i].dest_name = NULL;
929 /* Notify application */
930 COMMAND_REPLY((SILC_ARGS));
933 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
934 silc_client_command_reply_free(cmd);
937 /* Received reply for JOIN command. */
939 SILC_CLIENT_CMD_REPLY_FUNC(join)
941 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
942 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
943 SilcChannelEntry channel;
945 SilcChannelID *channel_id;
946 SilcUInt32 argc, mode = 0, len, list_count;
947 char *topic, *tmp, *channel_name = NULL, *hmac;
948 SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
949 SilcPublicKey founder_key = NULL;
950 SilcBufferStruct chpklist;
953 SILC_LOG_DEBUG(("Start"));
955 if (cmd->error != SILC_STATUS_OK) {
956 if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
957 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
958 "%s", silc_get_status_message(cmd->error));
963 argc = silc_argument_get_arg_num(cmd->args);
965 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
966 "Cannot join channel: Bad reply packet");
971 /* Get channel name */
972 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
974 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
975 "Cannot join channel: Bad reply packet");
982 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
984 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
985 "Cannot join channel: Bad reply packet");
989 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
995 /* Get channel mode */
996 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
998 SILC_GET32_MSB(mode, tmp);
1000 /* Get channel key */
1001 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
1003 keyp = silc_buffer_alloc(len);
1004 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
1005 silc_buffer_put(keyp, tmp, len);
1009 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1011 /* Check whether we have this channel entry already. */
1012 channel = silc_client_get_channel(cmd->client, conn, channel_name);
1014 if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1015 silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1017 /* Create new channel entry */
1018 channel = silc_client_add_channel(cmd->client, conn, channel_name,
1022 conn->current_channel = channel;
1025 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1027 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1028 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1029 "Cannot join channel: Unsupported HMAC `%s'", hmac);
1030 COMMAND_REPLY_ERROR;
1035 /* Get the list count */
1036 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1039 SILC_GET32_MSB(list_count, tmp);
1041 /* Get Client ID list */
1042 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1046 client_id_list = silc_buffer_alloc(len);
1047 silc_buffer_pull_tail(client_id_list, len);
1048 silc_buffer_put(client_id_list, tmp, len);
1050 /* Get client mode list */
1051 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1055 client_mode_list = silc_buffer_alloc(len);
1056 silc_buffer_pull_tail(client_mode_list, len);
1057 silc_buffer_put(client_mode_list, tmp, len);
1059 /* Add clients we received in the reply to the channel */
1060 for (i = 0; i < list_count; i++) {
1063 SilcClientID *client_id;
1064 SilcClientEntry client_entry;
1067 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1069 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1074 SILC_GET32_MSB(mode, client_mode_list->data);
1076 /* Check if we have this client cached already. */
1077 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1078 if (!client_entry) {
1079 /* No, we don't have it, add entry for it. */
1081 silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1082 silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1085 /* Join client to the channel */
1086 if (!silc_client_on_channel(channel, client_entry)) {
1087 chu = silc_calloc(1, sizeof(*chu));
1088 chu->client = client_entry;
1089 chu->channel = channel;
1091 silc_hash_table_add(channel->user_list, client_entry, chu);
1092 silc_hash_table_add(client_entry->channels, channel, chu);
1095 silc_free(client_id);
1096 silc_buffer_pull(client_id_list, idp_len);
1097 silc_buffer_pull(client_mode_list, 4);
1099 silc_buffer_push(client_id_list, client_id_list->data -
1100 client_id_list->head);
1101 silc_buffer_push(client_mode_list, client_mode_list->data -
1102 client_mode_list->head);
1104 /* Save channel key */
1105 if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1106 silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1108 /* Get founder key */
1109 tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
1111 silc_pkcs_public_key_payload_decode(tmp, len, &founder_key);
1113 /* Get channel public key list */
1114 tmp = silc_argument_get_arg_type(cmd->args, 16, &len);
1116 silc_buffer_set(&chpklist, tmp, len);
1118 /* Notify application */
1119 COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0,
1120 keyp ? keyp->head : NULL, NULL,
1121 NULL, topic, hmac, list_count, client_id_list,
1122 client_mode_list, founder_key, tmp ? &chpklist : NULL));
1125 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1126 silc_client_command_reply_free(cmd);
1128 silc_pkcs_public_key_free(founder_key);
1129 silc_buffer_free(keyp);
1130 silc_buffer_free(client_id_list);
1131 silc_buffer_free(client_mode_list);
1134 /* Received reply for MOTD command */
1136 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1138 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1139 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1141 char *motd = NULL, *cp, line[256];
1143 if (cmd->error != SILC_STATUS_OK) {
1144 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1145 "%s", silc_get_status_message(cmd->error));
1146 COMMAND_REPLY_ERROR;
1150 argc = silc_argument_get_arg_num(cmd->args);
1152 COMMAND_REPLY_ERROR;
1157 motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1159 COMMAND_REPLY_ERROR;
1166 if (cp[i++] == '\n') {
1167 memset(line, 0, sizeof(line));
1168 silc_strncat(line, sizeof(line), cp, i - 1);
1174 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1183 /* Notify application */
1184 COMMAND_REPLY((SILC_ARGS, motd));
1187 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1188 silc_client_command_reply_free(cmd);
1191 /* Received reply tot he UMODE command. Save the current user mode */
1193 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1195 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1196 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1200 if (cmd->error != SILC_STATUS_OK) {
1201 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1202 "%s", silc_get_status_message(cmd->error));
1203 COMMAND_REPLY_ERROR;
1207 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1209 COMMAND_REPLY_ERROR;
1213 SILC_GET32_MSB(mode, tmp);
1214 conn->local_entry->mode = mode;
1216 /* Notify application */
1217 COMMAND_REPLY((SILC_ARGS, mode));
1220 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1221 silc_client_command_reply_free(cmd);
1224 /* Received reply for CMODE command. */
1226 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1228 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1229 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1232 SilcChannelID *channel_id;
1233 SilcChannelEntry channel;
1235 SilcPublicKey public_key = NULL;
1236 SilcBufferStruct channel_pubkeys;
1238 if (cmd->error != SILC_STATUS_OK) {
1239 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1240 "%s", silc_get_status_message(cmd->error));
1241 COMMAND_REPLY_ERROR;
1245 /* Take Channel ID */
1246 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1249 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1253 /* Get the channel entry */
1254 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1256 silc_free(channel_id);
1257 COMMAND_REPLY_ERROR;
1261 /* Get channel mode */
1262 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1264 silc_free(channel_id);
1265 COMMAND_REPLY_ERROR;
1270 SILC_GET32_MSB(mode, tmp);
1271 channel->mode = mode;
1273 /* Get founder public key */
1274 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
1276 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1280 /* Get channel public key(s) */
1281 tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
1283 silc_buffer_set(&channel_pubkeys, tmp, len);
1285 /* Notify application */
1286 COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
1287 tmp ? &channel_pubkeys : NULL));
1289 silc_free(channel_id);
1293 silc_pkcs_public_key_free(public_key);
1294 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1295 silc_client_command_reply_free(cmd);
1298 /* Received reply for CUMODE command */
1300 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1302 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1303 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1304 SilcClientID *client_id;
1305 SilcChannelID *channel_id;
1306 SilcClientEntry client_entry;
1307 SilcChannelEntry channel;
1308 SilcChannelUser chu;
1309 unsigned char *modev, *tmp, *id;
1310 SilcUInt32 len, mode;
1312 if (cmd->error != SILC_STATUS_OK) {
1313 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1314 "%s", silc_get_status_message(cmd->error));
1315 COMMAND_REPLY_ERROR;
1319 /* Get channel mode */
1320 modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1322 COMMAND_REPLY_ERROR;
1326 /* Take Channel ID */
1327 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1330 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1334 /* Get the channel entry */
1335 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1337 silc_free(channel_id);
1338 COMMAND_REPLY_ERROR;
1343 id = silc_argument_get_arg_type(cmd->args, 4, &len);
1345 silc_free(channel_id);
1346 COMMAND_REPLY_ERROR;
1349 client_id = silc_id_payload_parse_id(id, len, NULL);
1351 silc_free(channel_id);
1352 COMMAND_REPLY_ERROR;
1356 /* Get client entry */
1357 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1358 if (!client_entry) {
1359 silc_free(channel_id);
1360 silc_free(client_id);
1361 COMMAND_REPLY_ERROR;
1366 SILC_GET32_MSB(mode, modev);
1367 chu = silc_client_on_channel(channel, client_entry);
1371 /* Notify application */
1372 COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1373 silc_free(client_id);
1374 silc_free(channel_id);
1377 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1378 silc_client_command_reply_free(cmd);
1381 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1383 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1384 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1386 if (cmd->error != SILC_STATUS_OK) {
1387 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1388 "%s", silc_get_status_message(cmd->error));
1389 COMMAND_REPLY_ERROR;
1393 /* Notify application */
1394 COMMAND_REPLY((SILC_ARGS));
1397 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1398 silc_client_command_reply_free(cmd);
1401 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1403 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1404 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1406 if (cmd->error != SILC_STATUS_OK) {
1407 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1408 "%s", silc_get_status_message(cmd->error));
1409 COMMAND_REPLY_ERROR;
1413 /* Notify application */
1414 COMMAND_REPLY((SILC_ARGS));
1417 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1418 silc_client_command_reply_free(cmd);
1421 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1423 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1424 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1426 if (cmd->error != SILC_STATUS_OK) {
1427 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1428 "%s", silc_get_status_message(cmd->error));
1429 COMMAND_REPLY_ERROR;
1433 /* Notify application */
1434 COMMAND_REPLY((SILC_ARGS));
1437 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1438 silc_client_command_reply_free(cmd);
1441 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1443 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1444 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1447 if (cmd->error != SILC_STATUS_OK) {
1448 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1449 "%s", silc_get_status_message(cmd->error));
1450 COMMAND_REPLY_ERROR;
1454 /* Notify application */
1455 COMMAND_REPLY((SILC_ARGS));
1457 /* Generate the detachment data and deliver it to the client in the
1458 detach client operation */
1459 detach = silc_client_get_detach_data(cmd->client, conn);
1461 cmd->client->internal->ops->detach(cmd->client, conn,
1462 detach->data, detach->len);
1463 silc_buffer_free(detach);
1467 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1468 silc_client_command_reply_free(cmd);
1471 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1473 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1474 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1476 if (cmd->error != SILC_STATUS_OK) {
1477 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1478 "%s", silc_get_status_message(cmd->error));
1479 COMMAND_REPLY_ERROR;
1483 /* Notify application */
1484 COMMAND_REPLY((SILC_ARGS));
1487 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1488 silc_client_command_reply_free(cmd);
1491 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1493 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1494 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1495 SilcChannelEntry channel;
1496 SilcChannelID *channel_id;
1499 SilcBufferStruct buf;
1501 if (cmd->error != SILC_STATUS_OK) {
1502 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1503 "%s", silc_get_status_message(cmd->error));
1504 COMMAND_REPLY_ERROR;
1508 /* Take Channel ID */
1509 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1513 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1517 /* Get the channel entry */
1518 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1520 silc_free(channel_id);
1521 COMMAND_REPLY_ERROR;
1525 /* Get the ban list */
1526 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1528 silc_buffer_set(&buf, tmp, len);
1530 /* Notify application */
1531 COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1534 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1535 silc_client_command_reply_free(cmd);
1538 /* Reply to LEAVE command. */
1540 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1542 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1543 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1544 SilcChannelID *channel_id;
1545 SilcChannelEntry channel = NULL;
1546 SilcChannelUser chu;
1550 if (cmd->error != SILC_STATUS_OK) {
1551 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1552 "%s", silc_get_status_message(cmd->error));
1553 COMMAND_REPLY_ERROR;
1557 /* From protocol version 1.1 we get the channel ID of the left channel */
1558 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1560 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1564 /* Get the channel entry */
1565 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1567 silc_free(channel_id);
1568 COMMAND_REPLY_ERROR;
1572 /* Remove us from this channel. */
1573 chu = silc_client_on_channel(channel, conn->local_entry);
1575 silc_hash_table_del(chu->client->channels, chu->channel);
1576 silc_hash_table_del(chu->channel->user_list, chu->client);
1580 silc_free(channel_id);
1583 /* Notify application */
1584 COMMAND_REPLY((SILC_ARGS, channel));
1586 /* Now delete the channel. */
1588 silc_client_del_channel(cmd->client, conn, channel);
1591 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1592 silc_client_command_reply_free(cmd);
1595 /* Channel resolving callback for USERS command reply. */
1597 static void silc_client_command_reply_users_cb(SilcClient client,
1598 SilcClientConnection conn,
1599 SilcChannelEntry *channels,
1600 SilcUInt32 channels_count,
1603 if (!channels_count) {
1604 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1605 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1607 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1608 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1609 "%s", silc_get_status_message(cmd->error));
1610 COMMAND_REPLY_ERROR;
1611 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1612 silc_client_command_reply_free(cmd);
1616 silc_client_command_reply_users(context, NULL);
1620 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1624 SilcGetChannelCallback get_channel,
1625 SilcCommandCb get_clients)
1627 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1628 SilcChannelEntry channel;
1629 SilcClientEntry client_entry;
1630 SilcChannelUser chu;
1631 SilcChannelID *channel_id = NULL;
1632 SilcBufferStruct client_id_list, client_mode_list;
1634 SilcUInt32 tmp_len, list_count;
1636 unsigned char **res_argv = NULL;
1637 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1638 bool wait_res = FALSE;
1640 SILC_LOG_DEBUG(("Start"));
1642 /* Get channel ID */
1643 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1645 COMMAND_REPLY_ERROR;
1648 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1650 COMMAND_REPLY_ERROR;
1654 /* Get the list count */
1655 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1657 COMMAND_REPLY_ERROR;
1660 SILC_GET32_MSB(list_count, tmp);
1662 /* Get Client ID list */
1663 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1665 COMMAND_REPLY_ERROR;
1668 silc_buffer_set(&client_id_list, tmp, tmp_len);
1670 /* Get client mode list */
1671 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1673 COMMAND_REPLY_ERROR;
1676 silc_buffer_set(&client_mode_list, tmp, tmp_len);
1678 /* Get channel entry */
1679 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1681 /* Resolve the channel from server */
1682 silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1684 silc_free(channel_id);
1688 SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1690 /* Cache the received Client ID's and modes. */
1691 for (i = 0; i < list_count; i++) {
1694 SilcClientID *client_id;
1697 SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1699 client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1704 SILC_GET32_MSB(mode, client_mode_list.data);
1706 /* Check if we have this client cached already. */
1707 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1708 if (!client_entry || !client_entry->username || !client_entry->realname) {
1710 /* No we don't have it (or it is incomplete in information), query
1711 it from the server. Assemble argument table that will be sent
1712 for the WHOIS command later. */
1713 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1715 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1717 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1719 res_argv[res_argc] = client_id_list.data;
1720 res_argv_lens[res_argc] = idp_len;
1721 res_argv_types[res_argc] = res_argc + 4;
1725 if (!silc_client_on_channel(channel, client_entry)) {
1726 chu = silc_calloc(1, sizeof(*chu));
1727 chu->client = client_entry;
1729 chu->channel = channel;
1730 silc_hash_table_add(channel->user_list, client_entry, chu);
1731 silc_hash_table_add(client_entry->channels, channel, chu);
1735 silc_free(client_id);
1736 silc_buffer_pull(&client_id_list, idp_len);
1737 silc_buffer_pull(&client_mode_list, 4);
1740 /* Query the client information from server if the list included clients
1741 that we don't know about. */
1745 /* Send the WHOIS command to server */
1746 silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1747 silc_client_command_reply_whois_i, 0,
1749 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1750 res_argc, res_argv, res_argv_lens,
1751 res_argv_types, conn->cmd_ident);
1752 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1753 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1756 /* Register pending command callback. After we've received the WHOIS
1757 command reply we will reprocess this command reply by re-calling this
1758 USERS command reply callback. */
1759 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1762 silc_buffer_free(res_cmd);
1763 silc_free(channel_id);
1764 silc_free(res_argv);
1765 silc_free(res_argv_lens);
1766 silc_free(res_argv_types);
1773 silc_buffer_push(&client_id_list, (client_id_list.data -
1774 client_id_list.head));
1775 silc_buffer_push(&client_mode_list, (client_mode_list.data -
1776 client_mode_list.head));
1778 /* Notify application */
1780 COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1781 &client_mode_list));
1784 silc_free(channel_id);
1788 /* Reply to USERS command. Received list of client ID's and theirs modes
1789 on the channel we requested. */
1791 SILC_CLIENT_CMD_REPLY_FUNC(users)
1793 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1794 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1795 SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
1797 SILC_LOG_DEBUG(("Start"));
1799 if (cmd->error != SILC_STATUS_OK) {
1800 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1801 "%s", silc_get_status_message(cmd->error));
1802 COMMAND_REPLY_ERROR;
1806 if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1807 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1808 /* Do not resolve anymore. Server may be sending us some non-existent
1809 Client ID (a bug in server), and we want to show the users list
1811 silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
1812 silc_client_command_reply_users_cb,
1813 silc_client_command_reply_users);
1816 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1817 "%s", silc_get_status_message(cmd->error));
1818 COMMAND_REPLY_ERROR;
1823 if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
1824 silc_client_command_reply_users_cb,
1825 silc_client_command_reply_users))
1829 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1830 silc_client_command_reply_free(cmd);
1833 /* Received command reply to GETKEY command. WE've received the remote
1834 client's public key. */
1836 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1838 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1839 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1840 SilcIDPayload idp = NULL;
1841 SilcClientID *client_id = NULL;
1842 SilcClientEntry client_entry;
1843 SilcServerID *server_id = NULL;
1844 SilcServerEntry server_entry;
1848 SilcPublicKey public_key = NULL;
1850 SILC_LOG_DEBUG(("Start"));
1852 if (cmd->error != SILC_STATUS_OK) {
1853 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1854 "%s", silc_get_status_message(cmd->error));
1855 COMMAND_REPLY_ERROR;
1859 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1861 COMMAND_REPLY_ERROR;
1864 idp = silc_id_payload_parse(tmp, len);
1866 COMMAND_REPLY_ERROR;
1870 /* Get the public key payload */
1871 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1873 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1877 id_type = silc_id_payload_get_type(idp);
1878 if (id_type == SILC_ID_CLIENT) {
1879 /* Received client's public key */
1880 client_id = silc_id_payload_get_id(idp);
1881 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1882 if (!client_entry) {
1883 COMMAND_REPLY_ERROR;
1887 /* Save fingerprint */
1888 if (!client_entry->fingerprint) {
1889 client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1890 client_entry->fingerprint_len = 20;
1891 silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1892 client_entry->fingerprint);
1895 /* Notify application */
1896 COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key));
1897 } else if (id_type == SILC_ID_SERVER) {
1898 /* Received server's public key */
1899 server_id = silc_id_payload_get_id(idp);
1900 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1901 if (!server_entry) {
1902 COMMAND_REPLY_ERROR;
1906 /* Notify application */
1907 COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1911 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1913 silc_id_payload_free(idp);
1915 silc_pkcs_public_key_free(public_key);
1916 silc_free(client_id);
1917 silc_free(server_id);
1918 silc_client_command_reply_free(cmd);
1921 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1923 silc_client_command_reply_free(context);
1927 /******************************************************************************
1929 Internal command reply functions
1931 ******************************************************************************/
1933 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1935 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1936 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1938 COMMAND_CHECK_STATUS_I;
1940 /* Save WHOIS info */
1941 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1943 /* Pending callbacks are not executed if this was an list entry */
1944 if (cmd->status != SILC_STATUS_OK &&
1945 cmd->status != SILC_STATUS_LIST_END) {
1946 silc_client_command_reply_free(cmd);
1951 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1954 /* If we received notify for invalid ID we'll remove the ID if we
1956 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1957 SilcClientEntry client_entry;
1959 unsigned char *tmp =
1960 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1963 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1965 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1968 silc_client_del_client(cmd->client, conn, client_entry);
1969 silc_free(client_id);
1974 /* Unregister this command reply */
1975 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1976 NULL, silc_client_command_reply_whois_i,
1979 silc_client_command_reply_free(cmd);
1982 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1984 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1985 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1987 COMMAND_CHECK_STATUS_I;
1989 /* Save IDENTIFY info */
1990 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1992 /* Pending callbacks are not executed if this was an list entry */
1993 if (cmd->status != SILC_STATUS_OK &&
1994 cmd->status != SILC_STATUS_LIST_END) {
1995 silc_client_command_reply_free(cmd);
2000 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
2003 /* If we received notify for invalid ID we'll remove the ID if we
2005 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
2006 SilcClientEntry client_entry;
2008 unsigned char *tmp =
2009 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
2012 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
2014 client_entry = silc_client_get_client_by_id(cmd->client, conn,
2017 silc_client_del_client(cmd->client, conn, client_entry);
2018 silc_free(client_id);
2023 /* Unregister this command reply */
2024 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
2025 NULL, silc_client_command_reply_identify_i,
2028 silc_client_command_reply_free(cmd);
2031 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2033 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2034 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2036 SilcServerEntry server;
2037 SilcServerID *server_id = NULL;
2038 char *server_name, *server_info;
2041 COMMAND_CHECK_STATUS_I;
2044 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2048 server_id = silc_id_payload_parse_id(tmp, len, NULL);
2052 /* Get server name */
2053 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2057 /* Get server info */
2058 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2062 /* See whether we have this server cached. If not create it. */
2063 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2065 SILC_LOG_DEBUG(("New server entry"));
2066 silc_client_add_server(cmd->client, conn, server_name, server_info,
2067 silc_id_dup(server_id, SILC_ID_SERVER));
2071 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2072 silc_free(server_id);
2074 silc_client_command_reply_free(cmd);
2077 static void silc_client_command_reply_users_i_cb(SilcClient client,
2078 SilcClientConnection conn,
2079 SilcChannelEntry *channels,
2080 SilcUInt32 channels_count,
2083 if (!channels_count) {
2084 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2085 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2087 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2088 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2089 "%s", silc_get_status_message(cmd->error));
2090 COMMAND_REPLY_ERROR;
2091 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2092 silc_client_command_reply_free(cmd);
2096 silc_client_command_reply_users_i(context, NULL);
2099 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2101 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2103 COMMAND_CHECK_STATUS_I;
2105 /* Save USERS info */
2106 if (silc_client_command_reply_users_save(
2107 cmd, cmd->status, FALSE, TRUE,
2108 silc_client_command_reply_users_i_cb,
2109 silc_client_command_reply_users_i))
2113 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2116 /* Unregister this command reply */
2117 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2118 NULL, silc_client_command_reply_users_i,
2121 silc_client_command_reply_free(cmd);
2124 /* Private range commands, specific to this implementation (and compatible
2125 with SILC Server >= 0.9). */
2127 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2129 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2130 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2132 if (cmd->error != SILC_STATUS_OK) {
2133 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2134 "%s", silc_get_status_message(cmd->error));
2135 COMMAND_REPLY_ERROR;
2139 /* Notify application */
2140 COMMAND_REPLY((SILC_ARGS));
2143 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2144 silc_client_command_reply_free(cmd);
2147 SILC_CLIENT_CMD_REPLY_FUNC(close)
2149 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2150 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2152 if (cmd->error != SILC_STATUS_OK) {
2153 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2154 "%s", silc_get_status_message(cmd->error));
2155 COMMAND_REPLY_ERROR;
2159 /* Notify application */
2160 COMMAND_REPLY((SILC_ARGS));
2163 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2164 silc_client_command_reply_free(cmd);
2167 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2169 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2170 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2172 if (cmd->error != SILC_STATUS_OK) {
2173 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2174 "%s", silc_get_status_message(cmd->error));
2175 COMMAND_REPLY_ERROR;
2179 /* Notify application */
2180 COMMAND_REPLY((SILC_ARGS));
2183 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2184 silc_client_command_reply_free(cmd);