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;
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->internal->client_cache,
576 silc_free(conn->nickname);
577 conn->nickname = strdup(tmp);
578 conn->local_entry->nickname = conn->nickname;
579 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
580 silc_idcache_add(conn->internal->client_cache, strdup(tmp),
581 conn->local_entry->id, conn->local_entry, 0, NULL);
584 /* Notify application */
585 COMMAND_REPLY((SILC_ARGS, conn->local_entry, conn->local_entry->nickname));
588 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
589 silc_client_command_reply_free(cmd);
592 /* Received reply to the LIST command. */
594 SILC_CLIENT_CMD_REPLY_FUNC(list)
596 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
597 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
598 unsigned char *tmp, *name, *topic;
599 SilcUInt32 usercount = 0, len;
600 SilcChannelID *channel_id = NULL;
601 SilcChannelEntry channel_entry;
603 COMMAND_CHECK_STATUS;
605 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
611 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
617 name = silc_argument_get_arg_type(cmd->args, 3, NULL);
623 topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
624 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
626 SILC_GET32_MSB(usercount, tmp);
628 /* Check whether the channel exists, and add it to cache if it doesn't. */
629 channel_entry = silc_client_get_channel_by_id(cmd->client, conn,
631 if (!channel_entry) {
632 /* Add new channel entry */
633 channel_entry = silc_client_add_channel(cmd->client, conn, name, 0,
635 if (!channel_entry) {
642 /* Notify application */
643 COMMAND_REPLY((SILC_ARGS, channel_entry, name, topic, usercount));
645 /* Pending callbacks are not executed if this was an list entry */
646 if (cmd->status != SILC_STATUS_OK &&
647 cmd->status != SILC_STATUS_LIST_END) {
648 silc_client_command_reply_free(cmd);
653 silc_free(channel_id);
654 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
656 silc_client_command_reply_free(cmd);
659 /* Received reply to topic command. */
661 SILC_CLIENT_CMD_REPLY_FUNC(topic)
663 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
664 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
665 SilcChannelEntry channel;
666 SilcChannelID *channel_id = NULL;
669 SilcUInt32 argc, len;
671 if (cmd->error != SILC_STATUS_OK) {
672 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
673 "%s", silc_get_status_message(cmd->error));
678 argc = silc_argument_get_arg_num(cmd->args);
679 if (argc < 1 || argc > 3) {
684 /* Take Channel ID */
685 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
690 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
694 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
698 /* Get the channel entry */
699 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
701 silc_free(channel_id);
706 /* Notify application */
707 COMMAND_REPLY((SILC_ARGS, channel, topic));
710 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
711 silc_client_command_reply_free(cmd);
714 /* Received reply to invite command. */
716 SILC_CLIENT_CMD_REPLY_FUNC(invite)
718 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
719 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
720 SilcChannelEntry channel;
721 SilcChannelID *channel_id;
724 SilcBufferStruct buf;
726 if (cmd->error != SILC_STATUS_OK) {
727 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
728 "%s", silc_get_status_message(cmd->error));
733 /* Take Channel ID */
734 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
738 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
742 /* Get the channel entry */
743 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
745 silc_free(channel_id);
750 /* Get the invite list */
751 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
753 silc_buffer_set(&buf, tmp, len);
755 /* Notify application */
756 COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
759 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
760 silc_client_command_reply_free(cmd);
763 /* Received reply to the KILL command. */
765 SILC_CLIENT_CMD_REPLY_FUNC(kill)
767 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
768 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
770 if (cmd->error != SILC_STATUS_OK) {
771 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
772 "%s", silc_get_status_message(cmd->error));
777 /* Notify application */
778 COMMAND_REPLY((SILC_ARGS));
781 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
782 silc_client_command_reply_free(cmd);
785 /* Received reply to INFO command. We receive the server ID and some
786 information about the server user requested. */
788 SILC_CLIENT_CMD_REPLY_FUNC(info)
790 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
791 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
793 SilcServerEntry server;
794 SilcServerID *server_id = NULL;
795 char *server_name, *server_info;
798 SILC_LOG_DEBUG(("Start"));
800 if (cmd->error != SILC_STATUS_OK) {
801 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
802 silc_get_status_message(cmd->error));
808 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
812 server_id = silc_id_payload_parse_id(tmp, len, NULL);
816 /* Get server name */
817 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
821 /* Get server info */
822 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
826 /* See whether we have this server cached. If not create it. */
827 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
829 SILC_LOG_DEBUG(("New server entry"));
830 server = silc_client_add_server(cmd->client, conn, server_name,
832 silc_id_dup(server_id, SILC_ID_SERVER));
837 /* Notify application */
838 COMMAND_REPLY((SILC_ARGS, server, server->server_name, server->server_info));
841 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
842 silc_free(server_id);
843 silc_client_command_reply_free(cmd);
846 /* Received reply to STATS command. */
848 SILC_CLIENT_CMD_REPLY_FUNC(stats)
850 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
851 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
852 unsigned char *tmp, *buf = NULL;
853 SilcUInt32 len, buf_len = 0;
855 if (cmd->error != SILC_STATUS_OK) {
856 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
857 "%s", silc_get_status_message(cmd->error));
863 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
867 /* Get statistics structure */
868 buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
870 /* Notify application */
871 COMMAND_REPLY((SILC_ARGS, buf, buf_len));
874 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
875 silc_client_command_reply_free(cmd);
878 /* Received reply to PING command. The reply time is shown to user. */
880 SILC_CLIENT_CMD_REPLY_FUNC(ping)
882 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
883 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
886 time_t diff, curtime;
888 if (cmd->error != SILC_STATUS_OK) {
889 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
890 "%s", silc_get_status_message(cmd->error));
895 curtime = time(NULL);
896 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
897 cmd->packet->src_id_type);
898 if (!id || !conn->internal->ping) {
903 for (i = 0; i < conn->internal->ping_count; i++) {
904 if (!conn->internal->ping[i].dest_id)
906 if (SILC_ID_SERVER_COMPARE(conn->internal->ping[i].dest_id, id)) {
907 diff = curtime - conn->internal->ping[i].start_time;
908 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
909 "Ping reply from %s: %d second%s",
910 conn->internal->ping[i].dest_name, diff,
911 diff == 1 ? "" : "s");
913 conn->internal->ping[i].start_time = 0;
914 silc_free(conn->internal->ping[i].dest_id);
915 conn->internal->ping[i].dest_id = NULL;
916 silc_free(conn->internal->ping[i].dest_name);
917 conn->internal->ping[i].dest_name = NULL;
924 /* Notify application */
925 COMMAND_REPLY((SILC_ARGS));
928 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
929 silc_client_command_reply_free(cmd);
932 /* Received reply for JOIN command. */
934 SILC_CLIENT_CMD_REPLY_FUNC(join)
936 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
937 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
938 SilcChannelEntry channel;
940 SilcChannelID *channel_id;
941 SilcUInt32 argc, mode = 0, len, list_count;
942 char *topic, *tmp, *channel_name = NULL, *hmac;
943 SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
944 SilcPublicKey founder_key = NULL;
945 SilcBufferStruct chpklist;
948 SILC_LOG_DEBUG(("Start"));
950 if (cmd->error != SILC_STATUS_OK) {
951 if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
952 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
953 "%s", silc_get_status_message(cmd->error));
958 argc = silc_argument_get_arg_num(cmd->args);
960 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
961 "Cannot join channel: Bad reply packet");
966 /* Get channel name */
967 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
969 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
970 "Cannot join channel: Bad reply packet");
977 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
979 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
980 "Cannot join channel: Bad reply packet");
984 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
990 /* Get channel mode */
991 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
993 SILC_GET32_MSB(mode, tmp);
995 /* Get channel key */
996 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
998 keyp = silc_buffer_alloc(len);
999 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
1000 silc_buffer_put(keyp, tmp, len);
1004 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1006 /* Check whether we have this channel entry already. */
1007 channel = silc_client_get_channel(cmd->client, conn, channel_name);
1009 if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1010 silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1012 /* Create new channel entry */
1013 channel = silc_client_add_channel(cmd->client, conn, channel_name,
1017 conn->current_channel = channel;
1020 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1022 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1023 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1024 "Cannot join channel: Unsupported HMAC `%s'", hmac);
1025 COMMAND_REPLY_ERROR;
1030 /* Get the list count */
1031 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1034 SILC_GET32_MSB(list_count, tmp);
1036 /* Get Client ID list */
1037 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1041 client_id_list = silc_buffer_alloc(len);
1042 silc_buffer_pull_tail(client_id_list, len);
1043 silc_buffer_put(client_id_list, tmp, len);
1045 /* Get client mode list */
1046 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1050 client_mode_list = silc_buffer_alloc(len);
1051 silc_buffer_pull_tail(client_mode_list, len);
1052 silc_buffer_put(client_mode_list, tmp, len);
1054 /* Add clients we received in the reply to the channel */
1055 for (i = 0; i < list_count; i++) {
1058 SilcClientID *client_id;
1059 SilcClientEntry client_entry;
1062 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1064 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1069 SILC_GET32_MSB(mode, client_mode_list->data);
1071 /* Check if we have this client cached already. */
1072 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1073 if (!client_entry) {
1074 /* No, we don't have it, add entry for it. */
1076 silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1077 silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1080 /* Join client to the channel */
1081 if (!silc_client_on_channel(channel, client_entry)) {
1082 chu = silc_calloc(1, sizeof(*chu));
1083 chu->client = client_entry;
1084 chu->channel = channel;
1086 silc_hash_table_add(channel->user_list, client_entry, chu);
1087 silc_hash_table_add(client_entry->channels, channel, chu);
1090 silc_free(client_id);
1091 silc_buffer_pull(client_id_list, idp_len);
1092 silc_buffer_pull(client_mode_list, 4);
1094 silc_buffer_push(client_id_list, client_id_list->data -
1095 client_id_list->head);
1096 silc_buffer_push(client_mode_list, client_mode_list->data -
1097 client_mode_list->head);
1099 /* Save channel key */
1100 if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1101 silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1103 /* Get founder key */
1104 tmp = silc_argument_get_arg_type(cmd->args, 15, &len);
1106 silc_pkcs_public_key_payload_decode(tmp, len, &founder_key);
1108 /* Get channel public key list */
1109 tmp = silc_argument_get_arg_type(cmd->args, 16, &len);
1111 silc_buffer_set(&chpklist, tmp, len);
1113 /* Notify application */
1114 COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0,
1115 keyp ? keyp->head : NULL, NULL,
1116 NULL, topic, hmac, list_count, client_id_list,
1117 client_mode_list, founder_key, tmp ? &chpklist : NULL));
1120 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1121 silc_client_command_reply_free(cmd);
1123 silc_pkcs_public_key_free(founder_key);
1124 silc_buffer_free(keyp);
1125 silc_buffer_free(client_id_list);
1126 silc_buffer_free(client_mode_list);
1129 /* Received reply for MOTD command */
1131 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1133 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1134 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1136 char *motd = NULL, *cp, line[256];
1138 if (cmd->error != SILC_STATUS_OK) {
1139 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1140 "%s", silc_get_status_message(cmd->error));
1141 COMMAND_REPLY_ERROR;
1145 argc = silc_argument_get_arg_num(cmd->args);
1147 COMMAND_REPLY_ERROR;
1152 motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1154 COMMAND_REPLY_ERROR;
1161 if (cp[i++] == '\n') {
1162 memset(line, 0, sizeof(line));
1163 silc_strncat(line, sizeof(line), cp, i - 1);
1169 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1178 /* Notify application */
1179 COMMAND_REPLY((SILC_ARGS, motd));
1182 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1183 silc_client_command_reply_free(cmd);
1186 /* Received reply tot he UMODE command. Save the current user mode */
1188 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1190 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1191 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1195 if (cmd->error != SILC_STATUS_OK) {
1196 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1197 "%s", silc_get_status_message(cmd->error));
1198 COMMAND_REPLY_ERROR;
1202 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1204 COMMAND_REPLY_ERROR;
1208 SILC_GET32_MSB(mode, tmp);
1209 conn->local_entry->mode = mode;
1211 /* Notify application */
1212 COMMAND_REPLY((SILC_ARGS, mode));
1215 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1216 silc_client_command_reply_free(cmd);
1219 /* Received reply for CMODE command. */
1221 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1223 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1224 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1227 SilcChannelID *channel_id;
1228 SilcChannelEntry channel;
1230 SilcPublicKey public_key = NULL;
1231 SilcBufferStruct channel_pubkeys;
1233 if (cmd->error != SILC_STATUS_OK) {
1234 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1235 "%s", silc_get_status_message(cmd->error));
1236 COMMAND_REPLY_ERROR;
1240 /* Take Channel ID */
1241 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1244 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1248 /* Get the channel entry */
1249 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1251 silc_free(channel_id);
1252 COMMAND_REPLY_ERROR;
1256 /* Get channel mode */
1257 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1259 silc_free(channel_id);
1260 COMMAND_REPLY_ERROR;
1265 SILC_GET32_MSB(mode, tmp);
1266 channel->mode = mode;
1268 /* Get founder public key */
1269 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
1271 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1275 /* Get channel public key(s) */
1276 tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
1278 silc_buffer_set(&channel_pubkeys, tmp, len);
1280 /* Notify application */
1281 COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
1282 tmp ? &channel_pubkeys : NULL));
1284 silc_free(channel_id);
1288 silc_pkcs_public_key_free(public_key);
1289 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1290 silc_client_command_reply_free(cmd);
1293 /* Received reply for CUMODE command */
1295 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1297 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1298 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1299 SilcClientID *client_id;
1300 SilcChannelID *channel_id;
1301 SilcClientEntry client_entry;
1302 SilcChannelEntry channel;
1303 SilcChannelUser chu;
1304 unsigned char *modev, *tmp, *id;
1305 SilcUInt32 len, mode;
1307 if (cmd->error != SILC_STATUS_OK) {
1308 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1309 "%s", silc_get_status_message(cmd->error));
1310 COMMAND_REPLY_ERROR;
1314 /* Get channel mode */
1315 modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1317 COMMAND_REPLY_ERROR;
1321 /* Take Channel ID */
1322 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1325 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1329 /* Get the channel entry */
1330 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1332 silc_free(channel_id);
1333 COMMAND_REPLY_ERROR;
1338 id = silc_argument_get_arg_type(cmd->args, 4, &len);
1340 silc_free(channel_id);
1341 COMMAND_REPLY_ERROR;
1344 client_id = silc_id_payload_parse_id(id, len, NULL);
1346 silc_free(channel_id);
1347 COMMAND_REPLY_ERROR;
1351 /* Get client entry */
1352 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1353 if (!client_entry) {
1354 silc_free(channel_id);
1355 silc_free(client_id);
1356 COMMAND_REPLY_ERROR;
1361 SILC_GET32_MSB(mode, modev);
1362 chu = silc_client_on_channel(channel, client_entry);
1366 /* Notify application */
1367 COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1368 silc_free(client_id);
1369 silc_free(channel_id);
1372 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1373 silc_client_command_reply_free(cmd);
1376 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1378 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1379 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1381 if (cmd->error != SILC_STATUS_OK) {
1382 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1383 "%s", silc_get_status_message(cmd->error));
1384 COMMAND_REPLY_ERROR;
1388 /* Notify application */
1389 COMMAND_REPLY((SILC_ARGS));
1392 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1393 silc_client_command_reply_free(cmd);
1396 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1398 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1399 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1401 if (cmd->error != SILC_STATUS_OK) {
1402 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1403 "%s", silc_get_status_message(cmd->error));
1404 COMMAND_REPLY_ERROR;
1408 /* Notify application */
1409 COMMAND_REPLY((SILC_ARGS));
1412 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1413 silc_client_command_reply_free(cmd);
1416 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1418 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1419 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1421 if (cmd->error != SILC_STATUS_OK) {
1422 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1423 "%s", silc_get_status_message(cmd->error));
1424 COMMAND_REPLY_ERROR;
1428 /* Notify application */
1429 COMMAND_REPLY((SILC_ARGS));
1432 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1433 silc_client_command_reply_free(cmd);
1436 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1438 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1439 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1442 if (cmd->error != SILC_STATUS_OK) {
1443 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1444 "%s", silc_get_status_message(cmd->error));
1445 COMMAND_REPLY_ERROR;
1449 /* Notify application */
1450 COMMAND_REPLY((SILC_ARGS));
1452 /* Generate the detachment data and deliver it to the client in the
1453 detach client operation */
1454 detach = silc_client_get_detach_data(cmd->client, conn);
1456 cmd->client->internal->ops->detach(cmd->client, conn,
1457 detach->data, detach->len);
1458 silc_buffer_free(detach);
1462 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1463 silc_client_command_reply_free(cmd);
1466 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1468 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1469 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1471 if (cmd->error != SILC_STATUS_OK) {
1472 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1473 "%s", silc_get_status_message(cmd->error));
1474 COMMAND_REPLY_ERROR;
1478 /* Notify application */
1479 COMMAND_REPLY((SILC_ARGS));
1482 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1483 silc_client_command_reply_free(cmd);
1486 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1488 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1489 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1490 SilcChannelEntry channel;
1491 SilcChannelID *channel_id;
1494 SilcBufferStruct buf;
1496 if (cmd->error != SILC_STATUS_OK) {
1497 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1498 "%s", silc_get_status_message(cmd->error));
1499 COMMAND_REPLY_ERROR;
1503 /* Take Channel ID */
1504 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1508 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1512 /* Get the channel entry */
1513 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1515 silc_free(channel_id);
1516 COMMAND_REPLY_ERROR;
1520 /* Get the ban list */
1521 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1523 silc_buffer_set(&buf, tmp, len);
1525 /* Notify application */
1526 COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1529 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1530 silc_client_command_reply_free(cmd);
1533 /* Reply to LEAVE command. */
1535 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1537 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1538 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1539 SilcChannelID *channel_id;
1540 SilcChannelEntry channel = NULL;
1544 if (cmd->error != SILC_STATUS_OK) {
1545 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1546 "%s", silc_get_status_message(cmd->error));
1547 COMMAND_REPLY_ERROR;
1551 /* From protocol version 1.1 we get the channel ID of the left channel */
1552 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1554 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1558 /* Get the channel entry */
1559 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1561 silc_free(channel_id);
1562 COMMAND_REPLY_ERROR;
1566 silc_free(channel_id);
1569 /* Notify application */
1570 COMMAND_REPLY((SILC_ARGS, channel));
1573 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1574 silc_client_command_reply_free(cmd);
1577 /* Channel resolving callback for USERS command reply. */
1579 static void silc_client_command_reply_users_cb(SilcClient client,
1580 SilcClientConnection conn,
1581 SilcChannelEntry *channels,
1582 SilcUInt32 channels_count,
1585 if (!channels_count) {
1586 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1587 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1589 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1590 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1591 "%s", silc_get_status_message(cmd->error));
1592 COMMAND_REPLY_ERROR;
1593 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1594 silc_client_command_reply_free(cmd);
1598 silc_client_command_reply_users(context, NULL);
1602 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1606 SilcGetChannelCallback get_channel,
1607 SilcCommandCb get_clients)
1609 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1610 SilcChannelEntry channel;
1611 SilcClientEntry client_entry;
1612 SilcChannelUser chu;
1613 SilcChannelID *channel_id = NULL;
1614 SilcBufferStruct client_id_list, client_mode_list;
1616 SilcUInt32 tmp_len, list_count;
1618 unsigned char **res_argv = NULL;
1619 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1620 bool wait_res = FALSE;
1622 SILC_LOG_DEBUG(("Start"));
1624 /* Get channel ID */
1625 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1627 COMMAND_REPLY_ERROR;
1630 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1632 COMMAND_REPLY_ERROR;
1636 /* Get the list count */
1637 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1639 COMMAND_REPLY_ERROR;
1642 SILC_GET32_MSB(list_count, tmp);
1644 /* Get Client ID list */
1645 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1647 COMMAND_REPLY_ERROR;
1650 silc_buffer_set(&client_id_list, tmp, tmp_len);
1652 /* Get client mode list */
1653 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1655 COMMAND_REPLY_ERROR;
1658 silc_buffer_set(&client_mode_list, tmp, tmp_len);
1660 /* Get channel entry */
1661 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1663 /* Resolve the channel from server */
1664 silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1666 silc_free(channel_id);
1670 SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1672 /* Cache the received Client ID's and modes. */
1673 for (i = 0; i < list_count; i++) {
1676 SilcClientID *client_id;
1679 SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1681 client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1686 SILC_GET32_MSB(mode, client_mode_list.data);
1688 /* Check if we have this client cached already. */
1689 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1690 if (!client_entry || !client_entry->username || !client_entry->realname) {
1692 /* No we don't have it (or it is incomplete in information), query
1693 it from the server. Assemble argument table that will be sent
1694 for the WHOIS command later. */
1695 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1697 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1699 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1701 res_argv[res_argc] = client_id_list.data;
1702 res_argv_lens[res_argc] = idp_len;
1703 res_argv_types[res_argc] = res_argc + 4;
1707 if (!silc_client_on_channel(channel, client_entry)) {
1708 chu = silc_calloc(1, sizeof(*chu));
1709 chu->client = client_entry;
1711 chu->channel = channel;
1712 silc_hash_table_add(channel->user_list, client_entry, chu);
1713 silc_hash_table_add(client_entry->channels, channel, chu);
1717 silc_free(client_id);
1718 silc_buffer_pull(&client_id_list, idp_len);
1719 silc_buffer_pull(&client_mode_list, 4);
1722 /* Query the client information from server if the list included clients
1723 that we don't know about. */
1727 /* Send the WHOIS command to server */
1728 silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1729 silc_client_command_reply_whois_i, 0,
1731 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1732 res_argc, res_argv, res_argv_lens,
1733 res_argv_types, conn->cmd_ident);
1734 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1735 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1738 /* Register pending command callback. After we've received the WHOIS
1739 command reply we will reprocess this command reply by re-calling this
1740 USERS command reply callback. */
1741 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1744 silc_buffer_free(res_cmd);
1745 silc_free(channel_id);
1746 silc_free(res_argv);
1747 silc_free(res_argv_lens);
1748 silc_free(res_argv_types);
1755 silc_buffer_push(&client_id_list, (client_id_list.data -
1756 client_id_list.head));
1757 silc_buffer_push(&client_mode_list, (client_mode_list.data -
1758 client_mode_list.head));
1760 /* Notify application */
1762 COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1763 &client_mode_list));
1766 silc_free(channel_id);
1770 /* Reply to USERS command. Received list of client ID's and theirs modes
1771 on the channel we requested. */
1773 SILC_CLIENT_CMD_REPLY_FUNC(users)
1775 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1776 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1777 SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
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 if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1789 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1790 /* Do not resolve anymore. Server may be sending us some non-existent
1791 Client ID (a bug in server), and we want to show the users list
1793 silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
1794 silc_client_command_reply_users_cb,
1795 silc_client_command_reply_users);
1798 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1799 "%s", silc_get_status_message(cmd->error));
1800 COMMAND_REPLY_ERROR;
1805 if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
1806 silc_client_command_reply_users_cb,
1807 silc_client_command_reply_users))
1811 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1812 silc_client_command_reply_free(cmd);
1815 /* Received command reply to GETKEY command. WE've received the remote
1816 client's public key. */
1818 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1820 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1821 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1822 SilcIDPayload idp = NULL;
1823 SilcClientID *client_id = NULL;
1824 SilcClientEntry client_entry;
1825 SilcServerID *server_id = NULL;
1826 SilcServerEntry server_entry;
1830 SilcPublicKey public_key = NULL;
1832 SILC_LOG_DEBUG(("Start"));
1834 if (cmd->error != SILC_STATUS_OK) {
1835 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1836 "%s", silc_get_status_message(cmd->error));
1837 COMMAND_REPLY_ERROR;
1841 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1843 COMMAND_REPLY_ERROR;
1846 idp = silc_id_payload_parse(tmp, len);
1848 COMMAND_REPLY_ERROR;
1852 /* Get the public key payload */
1853 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1855 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1859 id_type = silc_id_payload_get_type(idp);
1860 if (id_type == SILC_ID_CLIENT) {
1861 /* Received client's public key */
1862 client_id = silc_id_payload_get_id(idp);
1863 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1864 if (!client_entry) {
1865 COMMAND_REPLY_ERROR;
1869 /* Save fingerprint */
1870 if (!client_entry->fingerprint) {
1871 client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1872 client_entry->fingerprint_len = 20;
1873 silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1874 client_entry->fingerprint);
1877 /* Notify application */
1878 COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key));
1879 } else if (id_type == SILC_ID_SERVER) {
1880 /* Received server's public key */
1881 server_id = silc_id_payload_get_id(idp);
1882 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1883 if (!server_entry) {
1884 COMMAND_REPLY_ERROR;
1888 /* Notify application */
1889 COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1893 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1895 silc_id_payload_free(idp);
1897 silc_pkcs_public_key_free(public_key);
1898 silc_free(client_id);
1899 silc_free(server_id);
1900 silc_client_command_reply_free(cmd);
1903 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1905 silc_client_command_reply_free(context);
1909 /******************************************************************************
1911 Internal command reply functions
1913 ******************************************************************************/
1915 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1917 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1918 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1920 COMMAND_CHECK_STATUS_I;
1922 /* Save WHOIS info */
1923 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1925 /* Pending callbacks are not executed if this was an list entry */
1926 if (cmd->status != SILC_STATUS_OK &&
1927 cmd->status != SILC_STATUS_LIST_END) {
1928 silc_client_command_reply_free(cmd);
1933 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1936 /* If we received notify for invalid ID we'll remove the ID if we
1938 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1939 SilcClientEntry client_entry;
1941 unsigned char *tmp =
1942 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1945 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1947 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1950 silc_client_del_client(cmd->client, conn, client_entry);
1951 silc_free(client_id);
1956 /* Unregister this command reply */
1957 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1958 NULL, silc_client_command_reply_whois_i,
1961 silc_client_command_reply_free(cmd);
1964 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1966 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1967 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1969 COMMAND_CHECK_STATUS_I;
1971 /* Save IDENTIFY info */
1972 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1974 /* Pending callbacks are not executed if this was an list entry */
1975 if (cmd->status != SILC_STATUS_OK &&
1976 cmd->status != SILC_STATUS_LIST_END) {
1977 silc_client_command_reply_free(cmd);
1982 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1985 /* If we received notify for invalid ID we'll remove the ID if we
1987 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1988 SilcClientEntry client_entry;
1990 unsigned char *tmp =
1991 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1994 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1996 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1999 silc_client_del_client(cmd->client, conn, client_entry);
2000 silc_free(client_id);
2005 /* Unregister this command reply */
2006 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
2007 NULL, silc_client_command_reply_identify_i,
2010 silc_client_command_reply_free(cmd);
2013 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2015 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2016 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2018 SilcServerEntry server;
2019 SilcServerID *server_id = NULL;
2020 char *server_name, *server_info;
2023 COMMAND_CHECK_STATUS_I;
2026 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2030 server_id = silc_id_payload_parse_id(tmp, len, NULL);
2034 /* Get server name */
2035 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2039 /* Get server info */
2040 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2044 /* See whether we have this server cached. If not create it. */
2045 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2047 SILC_LOG_DEBUG(("New server entry"));
2048 silc_client_add_server(cmd->client, conn, server_name, server_info,
2049 silc_id_dup(server_id, SILC_ID_SERVER));
2053 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2054 silc_free(server_id);
2056 silc_client_command_reply_free(cmd);
2059 static void silc_client_command_reply_users_i_cb(SilcClient client,
2060 SilcClientConnection conn,
2061 SilcChannelEntry *channels,
2062 SilcUInt32 channels_count,
2065 if (!channels_count) {
2066 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2067 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2069 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2070 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2071 "%s", silc_get_status_message(cmd->error));
2072 COMMAND_REPLY_ERROR;
2073 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2074 silc_client_command_reply_free(cmd);
2078 silc_client_command_reply_users_i(context, NULL);
2081 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2083 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2085 COMMAND_CHECK_STATUS_I;
2087 /* Save USERS info */
2088 if (silc_client_command_reply_users_save(
2089 cmd, cmd->status, FALSE, TRUE,
2090 silc_client_command_reply_users_i_cb,
2091 silc_client_command_reply_users_i))
2095 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2098 /* Unregister this command reply */
2099 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2100 NULL, silc_client_command_reply_users_i,
2103 silc_client_command_reply_free(cmd);
2106 /* Private range commands, specific to this implementation (and compatible
2107 with SILC Server >= 0.9). */
2109 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2111 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2112 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2114 if (cmd->error != SILC_STATUS_OK) {
2115 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2116 "%s", silc_get_status_message(cmd->error));
2117 COMMAND_REPLY_ERROR;
2121 /* Notify application */
2122 COMMAND_REPLY((SILC_ARGS));
2125 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2126 silc_client_command_reply_free(cmd);
2129 SILC_CLIENT_CMD_REPLY_FUNC(close)
2131 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2132 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2134 if (cmd->error != SILC_STATUS_OK) {
2135 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2136 "%s", silc_get_status_message(cmd->error));
2137 COMMAND_REPLY_ERROR;
2141 /* Notify application */
2142 COMMAND_REPLY((SILC_ARGS));
2145 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2146 silc_client_command_reply_free(cmd);
2149 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2151 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2152 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2154 if (cmd->error != SILC_STATUS_OK) {
2155 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2156 "%s", silc_get_status_message(cmd->error));
2157 COMMAND_REPLY_ERROR;
2161 /* Notify application */
2162 COMMAND_REPLY((SILC_ARGS));
2165 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2166 silc_client_command_reply_free(cmd);