5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 * Command reply functions are "the otherside" of the command functions.
22 * Reply to a command sent by server is handled by these functions.
24 * The arguments received from server are also passed to the calling
25 * application through command_reply client operation. The arguments are
26 * exactly same and in same order as the server sent it. However, ID's are
27 * not sent to the application. Instead, corresponding ID entry is sent
28 * to the application. For example, instead of sending Client ID the
29 * corresponding SilcClientEntry is sent to the application. The case is
30 * same with for example Channel ID's. This way application has all the
31 * necessary data already in hand without redundant searching. If ID is
32 * received but ID entry does not exist, NULL is sent.
35 #include "silcincludes.h"
36 #include "silcclient.h"
37 #include "client_internal.h"
39 #define SAY cmd->client->internal->ops->say
41 /* All functions that call the COMMAND_CHECK_STATUS macro must have
42 out: and err: goto labels. out label should call the pending
43 command replies, and the err label just handle error condition. */
45 #define COMMAND_CHECK_STATUS \
47 SILC_LOG_DEBUG(("Start")); \
48 if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \
49 if (SILC_STATUS_IS_ERROR(cmd->status)) { \
51 COMMAND_REPLY_ERROR; \
54 /* List of errors */ \
55 COMMAND_REPLY_ERROR; \
56 if (cmd->status == SILC_STATUS_LIST_END) \
62 /* Same as COMMAND_CHECK_STATUS but doesn't call client operation */
63 #define COMMAND_CHECK_STATUS_I \
65 SILC_LOG_DEBUG(("Start")); \
66 if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \
67 if (SILC_STATUS_IS_ERROR(cmd->status)) \
69 if (cmd->status == SILC_STATUS_LIST_END) \
75 /* Process received command reply. */
77 void silc_client_command_reply_process(SilcClient client,
78 SilcSocketConnection sock,
79 SilcPacketContext *packet)
81 SilcBuffer buffer = packet->buffer;
82 SilcClientCommand cmd;
83 SilcClientCommandReplyContext ctx;
84 SilcCommandPayload payload;
86 SilcCommandCb reply = NULL;
88 /* Get command reply payload from packet */
89 payload = silc_command_payload_parse(buffer->data, buffer->len);
91 /* Silently ignore bad reply packet */
92 SILC_LOG_DEBUG(("Bad command reply packet"));
96 /* Allocate command reply context. This must be free'd by the
97 command reply routine receiving it. */
98 ctx = silc_calloc(1, sizeof(*ctx));
100 ctx->client = client;
102 ctx->payload = payload;
103 ctx->args = silc_command_get_args(ctx->payload);
104 ctx->packet = packet;
105 ctx->ident = silc_command_get_ident(ctx->payload);
106 silc_command_get_status(ctx->payload, &ctx->status, &ctx->error);
108 /* Check for pending commands and mark to be exeucted */
110 silc_client_command_pending_check(sock->user_data, ctx,
111 silc_command_get(ctx->payload),
112 ctx->ident, &ctx->callbacks_count);
114 /* Execute command reply */
116 command = silc_command_get(ctx->payload);
118 /* Try to find matching the command identifier */
119 silc_list_start(client->internal->commands);
120 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
121 if (cmd->cmd == command && !cmd->ident)
123 if (cmd->cmd == command && cmd->ident == ctx->ident) {
124 (*cmd->reply)((void *)ctx, NULL);
129 if (cmd == SILC_LIST_END) {
131 /* No specific identifier for command reply, call first one found */
138 /* Duplicate Command Reply Context by adding reference counter. The context
139 won't be free'd untill it hits zero. */
141 SilcClientCommandReplyContext
142 silc_client_command_reply_dup(SilcClientCommandReplyContext cmd)
145 SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd,
146 cmd->users - 1, cmd->users));
150 /* Free command reply context and its internals. */
152 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
155 SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd,
156 cmd->users + 1, cmd->users));
157 if (cmd->users < 1) {
158 silc_command_payload_free(cmd->payload);
164 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
168 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
169 SilcClientID *client_id;
170 SilcClientEntry client_entry = NULL;
172 unsigned char *id_data, *tmp;
173 char *nickname = NULL, *username = NULL;
174 char *realname = NULL;
175 SilcUInt32 idle = 0, mode = 0;
176 SilcBufferStruct channels, ch_user_modes;
177 bool has_channels = FALSE, has_user_modes = FALSE;
178 unsigned char *fingerprint;
179 SilcUInt32 fingerprint_len;
181 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
188 client_id = silc_id_payload_parse_id(id_data, len, NULL);
195 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
196 username = silc_argument_get_arg_type(cmd->args, 4, &len);
197 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
198 if (!nickname || !username || !realname) {
204 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
206 silc_buffer_set(&channels, tmp, len);
210 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
212 SILC_GET32_MSB(mode, tmp);
214 tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
216 SILC_GET32_MSB(idle, tmp);
218 fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
220 tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
222 silc_buffer_set(&ch_user_modes, tmp, len);
223 has_user_modes = TRUE;
226 /* Check if we have this client cached already. */
227 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
229 SILC_LOG_DEBUG(("Adding new client entry"));
231 silc_client_add_client(cmd->client, conn, nickname, username, realname,
234 silc_client_update_client(cmd->client, conn, client_entry,
235 nickname, username, realname, mode);
236 silc_free(client_id);
239 if (fingerprint && !client_entry->fingerprint) {
240 client_entry->fingerprint = silc_memdup(fingerprint, fingerprint_len);
241 client_entry->fingerprint_len = fingerprint_len;
244 /* Take Requested Attributes if set. */
245 tmp = silc_argument_get_arg_type(cmd->args, 11, &len);
247 if (client_entry->attrs)
248 silc_attribute_payload_list_free(client_entry->attrs);
249 client_entry->attrs = silc_attribute_payload_parse(tmp, len);
252 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
254 /* Notify application */
255 if (!cmd->callbacks_count && notify)
256 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
257 has_channels ? &channels : NULL, mode, idle,
258 fingerprint, has_user_modes ? &ch_user_modes : NULL,
259 client_entry->attrs));
262 /* Received reply for WHOIS command. This maybe called several times
263 for one WHOIS command as server may reply with list of results. */
265 SILC_CLIENT_CMD_REPLY_FUNC(whois)
267 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
268 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
270 COMMAND_CHECK_STATUS;
272 /* Save WHOIS info */
273 silc_client_command_reply_whois_save(cmd, cmd->status, TRUE);
275 /* Pending callbacks are not executed if this was an list entry */
276 if (cmd->status != SILC_STATUS_OK &&
277 cmd->status != SILC_STATUS_LIST_END) {
278 silc_client_command_reply_free(cmd);
283 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
286 /* If we received notify for invalid ID we'll remove the ID if we
288 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
289 SilcClientEntry client_entry;
292 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
295 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
297 client_entry = silc_client_get_client_by_id(cmd->client, conn,
300 silc_client_del_client(cmd->client, conn, client_entry);
301 silc_free(client_id);
306 silc_client_command_reply_free(cmd);
309 /* Received reply for WHOWAS command. */
311 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
313 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
314 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
315 SilcClientID *client_id;
316 SilcClientEntry client_entry = NULL;
318 unsigned char *id_data;
319 char *nickname, *username;
320 char *realname = NULL;
322 COMMAND_CHECK_STATUS;
324 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
330 client_id = silc_id_payload_parse_id(id_data, len, NULL);
336 /* Get the client entry, if exists */
337 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
338 silc_free(client_id);
340 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
341 username = silc_argument_get_arg_type(cmd->args, 4, &len);
342 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
343 if (!nickname || !username) {
348 /* Notify application. We don't save any history information to any
349 cache. Just pass the data to the application for displaying on
351 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
353 /* Pending callbacks are not executed if this was an list entry */
354 if (cmd->status != SILC_STATUS_OK &&
355 cmd->status != SILC_STATUS_LIST_END) {
356 silc_client_command_reply_free(cmd);
361 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
363 silc_client_command_reply_free(cmd);
367 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
371 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
372 SilcClient client = cmd->client;
373 SilcClientID *client_id = NULL;
374 SilcServerID *server_id = NULL;
375 SilcChannelID *channel_id = NULL;
376 SilcClientEntry client_entry;
377 SilcServerEntry server_entry;
378 SilcChannelEntry channel_entry;
380 unsigned char *id_data;
381 char *name = NULL, *info = NULL;
382 SilcIDPayload idp = NULL;
385 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
391 idp = silc_id_payload_parse(id_data, len);
398 name = silc_argument_get_arg_type(cmd->args, 3, &len);
399 info = silc_argument_get_arg_type(cmd->args, 4, &len);
401 id_type = silc_id_payload_get_type(idp);
405 client_id = silc_id_payload_get_id(idp);
407 SILC_LOG_DEBUG(("Received client information"));
409 /* Check if we have this client cached already. */
410 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
412 SILC_LOG_DEBUG(("Adding new client entry"));
414 silc_client_add_client(cmd->client, conn, name, info, NULL,
415 silc_id_dup(client_id, id_type), 0);
417 silc_client_update_client(cmd->client, conn, client_entry,
418 name, info, NULL, 0);
421 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
423 /* Notify application */
425 COMMAND_REPLY((ARGS, client_entry, name, info));
429 server_id = silc_id_payload_get_id(idp);
431 SILC_LOG_DEBUG(("Received server information"));
433 /* Check if we have this server cached already. */
434 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
436 SILC_LOG_DEBUG(("Adding new server entry"));
437 server_entry = silc_client_add_server(cmd->client, conn, name, info,
438 silc_id_dup(server_id, id_type));
445 silc_client_update_server(client, conn, server_entry, name, info);
448 server_entry->resolve_cmd_ident = 0;
450 /* Notify application */
452 COMMAND_REPLY((ARGS, server_entry, name, info));
455 case SILC_ID_CHANNEL:
456 channel_id = silc_id_payload_get_id(idp);
458 SILC_LOG_DEBUG(("Received channel information"));
460 /* Check if we have this channel cached already. */
461 channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
462 if (!channel_entry) {
466 /* Add new channel entry */
467 channel_entry = silc_client_add_channel(client, conn, name, 0,
472 /* Notify application */
474 COMMAND_REPLY((ARGS, channel_entry, name, info));
478 silc_id_payload_free(idp);
479 silc_free(client_id);
480 silc_free(server_id);
481 silc_free(channel_id);
484 /* Received reply for IDENTIFY command. This maybe called several times
485 for one IDENTIFY command as server may reply with list of results.
486 This is totally silent and does not print anything on screen. */
488 SILC_CLIENT_CMD_REPLY_FUNC(identify)
490 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
491 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
493 COMMAND_CHECK_STATUS;
495 /* Save IDENTIFY info */
496 silc_client_command_reply_identify_save(cmd, cmd->status, TRUE);
498 /* Pending callbacks are not executed if this was an list entry */
499 if (cmd->status != SILC_STATUS_OK &&
500 cmd->status != SILC_STATUS_LIST_END) {
501 silc_client_command_reply_free(cmd);
506 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
509 /* If we received notify for invalid ID we'll remove the ID if we
511 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
512 SilcClientEntry client_entry;
515 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
518 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
520 client_entry = silc_client_get_client_by_id(cmd->client, conn,
523 silc_client_del_client(cmd->client, conn, client_entry);
524 silc_free(client_id);
529 silc_client_command_reply_free(cmd);
532 /* Received reply for command NICK. If everything went without errors
533 we just received our new Client ID. */
535 SILC_CLIENT_CMD_REPLY_FUNC(nick)
537 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
538 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
541 SilcUInt32 argc, len;
543 SILC_LOG_DEBUG(("Start"));
545 if (cmd->error != SILC_STATUS_OK) {
546 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
547 "Cannot set nickname: %s",
548 silc_get_status_message(cmd->error));
553 argc = silc_argument_get_arg_num(cmd->args);
554 if (argc < 2 || argc > 3) {
555 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
556 "Cannot set nickname: bad reply to command");
561 /* Take received Client ID */
562 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
563 idp = silc_id_payload_parse(tmp, len);
568 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
570 /* Take the new nickname too */
571 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
573 silc_idcache_del_by_context(conn->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((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((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((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;
725 if (cmd->error != SILC_STATUS_OK) {
726 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
727 "%s", silc_get_status_message(cmd->error));
732 /* Take Channel ID */
733 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
737 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
741 /* Get the channel entry */
742 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
744 silc_free(channel_id);
749 /* Get the invite list */
750 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
752 /* Notify application */
753 COMMAND_REPLY((ARGS, channel, tmp));
756 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
757 silc_client_command_reply_free(cmd);
760 /* Received reply to the KILL command. */
762 SILC_CLIENT_CMD_REPLY_FUNC(kill)
764 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
765 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
767 if (cmd->error != SILC_STATUS_OK) {
768 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
769 "%s", silc_get_status_message(cmd->error));
774 /* Notify application */
775 COMMAND_REPLY((ARGS));
778 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
779 silc_client_command_reply_free(cmd);
782 /* Received reply to INFO command. We receive the server ID and some
783 information about the server user requested. */
785 SILC_CLIENT_CMD_REPLY_FUNC(info)
787 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
788 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
790 SilcServerEntry server;
791 SilcServerID *server_id = NULL;
792 char *server_name, *server_info;
795 SILC_LOG_DEBUG(("Start"));
797 if (cmd->error != SILC_STATUS_OK) {
798 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
799 silc_get_status_message(cmd->error));
805 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
809 server_id = silc_id_payload_parse_id(tmp, len, NULL);
813 /* Get server name */
814 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
818 /* Get server info */
819 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
823 /* See whether we have this server cached. If not create it. */
824 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
826 SILC_LOG_DEBUG(("New server entry"));
827 server = silc_client_add_server(cmd->client, conn, server_name,
829 silc_id_dup(server_id, SILC_ID_SERVER));
834 /* Notify application */
835 COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
838 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
839 silc_free(server_id);
840 silc_client_command_reply_free(cmd);
843 /* Received reply to STATS command. */
845 SILC_CLIENT_CMD_REPLY_FUNC(stats)
847 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
848 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
849 unsigned char *tmp, *buf = NULL;
850 SilcUInt32 len, buf_len = 0;
852 if (cmd->error != SILC_STATUS_OK) {
853 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
854 "%s", silc_get_status_message(cmd->error));
860 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
864 /* Get statistics structure */
865 buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len);
867 /* Notify application */
868 COMMAND_REPLY((ARGS, buf, buf_len));
871 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
872 silc_client_command_reply_free(cmd);
875 /* Received reply to PING command. The reply time is shown to user. */
877 SILC_CLIENT_CMD_REPLY_FUNC(ping)
879 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
880 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
883 time_t diff, curtime;
885 if (cmd->error != SILC_STATUS_OK) {
886 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
887 "%s", silc_get_status_message(cmd->error));
892 curtime = time(NULL);
893 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
894 cmd->packet->src_id_type);
895 if (!id || !conn->internal->ping) {
900 for (i = 0; i < conn->internal->ping_count; i++) {
901 if (!conn->internal->ping[i].dest_id)
903 if (SILC_ID_SERVER_COMPARE(conn->internal->ping[i].dest_id, id)) {
904 diff = curtime - conn->internal->ping[i].start_time;
905 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
906 "Ping reply from %s: %d second%s",
907 conn->internal->ping[i].dest_name, diff,
908 diff == 1 ? "" : "s");
910 conn->internal->ping[i].start_time = 0;
911 silc_free(conn->internal->ping[i].dest_id);
912 conn->internal->ping[i].dest_id = NULL;
913 silc_free(conn->internal->ping[i].dest_name);
914 conn->internal->ping[i].dest_name = NULL;
921 /* Notify application */
922 COMMAND_REPLY((ARGS));
925 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
926 silc_client_command_reply_free(cmd);
929 /* Received reply for JOIN command. */
931 SILC_CLIENT_CMD_REPLY_FUNC(join)
933 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
934 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
935 SilcChannelEntry channel;
937 SilcChannelID *channel_id;
938 SilcUInt32 argc, mode = 0, len, list_count;
939 char *topic, *tmp, *channel_name = NULL, *hmac;
940 SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
943 SILC_LOG_DEBUG(("Start"));
945 if (cmd->error != SILC_STATUS_OK) {
946 if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
947 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
948 "%s", silc_get_status_message(cmd->error));
953 argc = silc_argument_get_arg_num(cmd->args);
955 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
956 "Cannot join channel: Bad reply packet");
961 /* Get channel name */
962 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
964 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
965 "Cannot join channel: Bad reply packet");
972 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
974 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
975 "Cannot join channel: Bad reply packet");
979 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
985 /* Get channel mode */
986 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
988 SILC_GET32_MSB(mode, tmp);
990 /* Get channel key */
991 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
993 keyp = silc_buffer_alloc(len);
994 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
995 silc_buffer_put(keyp, tmp, len);
999 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1001 /* Check whether we have this channel entry already. */
1002 channel = silc_client_get_channel(cmd->client, conn, channel_name);
1004 if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1005 silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1007 /* Create new channel entry */
1008 channel = silc_client_add_channel(cmd->client, conn, channel_name,
1012 conn->current_channel = channel;
1015 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1017 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1018 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1019 "Cannot join channel: Unsupported HMAC `%s'", hmac);
1020 COMMAND_REPLY_ERROR;
1025 /* Get the list count */
1026 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1029 SILC_GET32_MSB(list_count, tmp);
1031 /* Get Client ID list */
1032 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1036 client_id_list = silc_buffer_alloc(len);
1037 silc_buffer_pull_tail(client_id_list, len);
1038 silc_buffer_put(client_id_list, tmp, len);
1040 /* Get client mode list */
1041 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1045 client_mode_list = silc_buffer_alloc(len);
1046 silc_buffer_pull_tail(client_mode_list, len);
1047 silc_buffer_put(client_mode_list, tmp, len);
1049 /* Add clients we received in the reply to the channel */
1050 for (i = 0; i < list_count; i++) {
1053 SilcClientID *client_id;
1054 SilcClientEntry client_entry;
1057 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1059 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1064 SILC_GET32_MSB(mode, client_mode_list->data);
1066 /* Check if we have this client cached already. */
1067 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1068 if (!client_entry) {
1069 /* No, we don't have it, add entry for it. */
1071 silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1072 silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1075 /* Join client to the channel */
1076 if (!silc_client_on_channel(channel, client_entry)) {
1077 chu = silc_calloc(1, sizeof(*chu));
1078 chu->client = client_entry;
1079 chu->channel = channel;
1081 silc_hash_table_add(channel->user_list, client_entry, chu);
1082 silc_hash_table_add(client_entry->channels, channel, chu);
1085 silc_free(client_id);
1086 silc_buffer_pull(client_id_list, idp_len);
1087 silc_buffer_pull(client_mode_list, 4);
1089 silc_buffer_push(client_id_list, client_id_list->data -
1090 client_id_list->head);
1091 silc_buffer_push(client_mode_list, client_mode_list->data -
1092 client_mode_list->head);
1094 /* Save channel key */
1095 if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1096 silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1098 /* Notify application */
1099 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0,
1100 keyp ? keyp->head : NULL, NULL,
1101 NULL, topic, hmac, list_count, client_id_list,
1105 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1106 silc_client_command_reply_free(cmd);
1109 silc_buffer_free(keyp);
1111 silc_buffer_free(client_id_list);
1112 if (client_mode_list)
1113 silc_buffer_free(client_mode_list);
1116 /* Received reply for MOTD command */
1118 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1120 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1121 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1123 char *motd = NULL, *cp, line[256];
1125 if (cmd->error != SILC_STATUS_OK) {
1126 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1127 "%s", silc_get_status_message(cmd->error));
1128 COMMAND_REPLY_ERROR;
1132 argc = silc_argument_get_arg_num(cmd->args);
1134 COMMAND_REPLY_ERROR;
1139 motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1141 COMMAND_REPLY_ERROR;
1148 if (cp[i++] == '\n') {
1149 memset(line, 0, sizeof(line));
1150 silc_strncat(line, sizeof(line), cp, i - 1);
1156 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1165 /* Notify application */
1166 COMMAND_REPLY((ARGS, motd));
1169 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1170 silc_client_command_reply_free(cmd);
1173 /* Received reply tot he UMODE command. Save the current user mode */
1175 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1177 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1178 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1182 if (cmd->error != SILC_STATUS_OK) {
1183 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1184 "%s", silc_get_status_message(cmd->error));
1185 COMMAND_REPLY_ERROR;
1189 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1191 COMMAND_REPLY_ERROR;
1195 SILC_GET32_MSB(mode, tmp);
1196 conn->local_entry->mode = mode;
1198 /* Notify application */
1199 COMMAND_REPLY((ARGS, mode));
1202 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1203 silc_client_command_reply_free(cmd);
1206 /* Received reply for CMODE command. */
1208 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1210 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1211 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1214 SilcChannelID *channel_id;
1215 SilcChannelEntry channel;
1218 if (cmd->error != SILC_STATUS_OK) {
1219 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1220 "%s", silc_get_status_message(cmd->error));
1221 COMMAND_REPLY_ERROR;
1225 /* Take Channel ID */
1226 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1229 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1233 /* Get the channel entry */
1234 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1236 silc_free(channel_id);
1237 COMMAND_REPLY_ERROR;
1241 /* Get channel mode */
1242 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1244 silc_free(channel_id);
1245 COMMAND_REPLY_ERROR;
1250 SILC_GET32_MSB(mode, tmp);
1251 channel->mode = mode;
1253 /* Notify application */
1254 COMMAND_REPLY((ARGS, channel, mode));
1256 silc_free(channel_id);
1259 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1260 silc_client_command_reply_free(cmd);
1263 /* Received reply for CUMODE command */
1265 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1267 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1268 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1269 SilcClientID *client_id;
1270 SilcChannelID *channel_id;
1271 SilcClientEntry client_entry;
1272 SilcChannelEntry channel;
1273 SilcChannelUser chu;
1274 unsigned char *modev, *tmp, *id;
1275 SilcUInt32 len, mode;
1277 if (cmd->error != SILC_STATUS_OK) {
1278 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1279 "%s", silc_get_status_message(cmd->error));
1280 COMMAND_REPLY_ERROR;
1284 /* Get channel mode */
1285 modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1287 COMMAND_REPLY_ERROR;
1291 /* Take Channel ID */
1292 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1295 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1299 /* Get the channel entry */
1300 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1302 silc_free(channel_id);
1303 COMMAND_REPLY_ERROR;
1308 id = silc_argument_get_arg_type(cmd->args, 4, &len);
1310 silc_free(channel_id);
1311 COMMAND_REPLY_ERROR;
1314 client_id = silc_id_payload_parse_id(id, len, NULL);
1316 silc_free(channel_id);
1317 COMMAND_REPLY_ERROR;
1321 /* Get client entry */
1322 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1323 if (!client_entry) {
1324 silc_free(channel_id);
1325 silc_free(client_id);
1326 COMMAND_REPLY_ERROR;
1331 SILC_GET32_MSB(mode, modev);
1332 chu = silc_client_on_channel(channel, client_entry);
1336 /* Notify application */
1337 COMMAND_REPLY((ARGS, mode, channel, client_entry));
1338 silc_free(client_id);
1339 silc_free(channel_id);
1342 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1343 silc_client_command_reply_free(cmd);
1346 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1348 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1349 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1351 if (cmd->error != SILC_STATUS_OK) {
1352 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1353 "%s", silc_get_status_message(cmd->error));
1354 COMMAND_REPLY_ERROR;
1358 /* Notify application */
1359 COMMAND_REPLY((ARGS));
1362 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1363 silc_client_command_reply_free(cmd);
1366 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1368 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1369 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1371 if (cmd->error != SILC_STATUS_OK) {
1372 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1373 "%s", silc_get_status_message(cmd->error));
1374 COMMAND_REPLY_ERROR;
1378 /* Notify application */
1379 COMMAND_REPLY((ARGS));
1382 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1383 silc_client_command_reply_free(cmd);
1386 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1388 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1389 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1391 if (cmd->error != SILC_STATUS_OK) {
1392 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1393 "%s", silc_get_status_message(cmd->error));
1394 COMMAND_REPLY_ERROR;
1398 /* Notify application */
1399 COMMAND_REPLY((ARGS));
1402 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1403 silc_client_command_reply_free(cmd);
1406 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1408 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1409 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1412 if (cmd->error != SILC_STATUS_OK) {
1413 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1414 "%s", silc_get_status_message(cmd->error));
1415 COMMAND_REPLY_ERROR;
1419 /* Notify application */
1420 COMMAND_REPLY((ARGS));
1422 /* Generate the detachment data and deliver it to the client in the
1423 detach client operation */
1424 detach = silc_client_get_detach_data(cmd->client, conn);
1426 cmd->client->internal->ops->detach(cmd->client, conn,
1427 detach->data, detach->len);
1428 silc_buffer_free(detach);
1432 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1433 silc_client_command_reply_free(cmd);
1436 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1438 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1439 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1441 if (cmd->error != SILC_STATUS_OK) {
1442 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1443 "%s", silc_get_status_message(cmd->error));
1444 COMMAND_REPLY_ERROR;
1448 /* Notify application */
1449 COMMAND_REPLY((ARGS));
1452 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1453 silc_client_command_reply_free(cmd);
1456 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1458 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1459 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1460 SilcChannelEntry channel;
1461 SilcChannelID *channel_id;
1465 if (cmd->error != SILC_STATUS_OK) {
1466 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1467 "%s", silc_get_status_message(cmd->error));
1468 COMMAND_REPLY_ERROR;
1472 /* Take Channel ID */
1473 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1477 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1481 /* Get the channel entry */
1482 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1484 silc_free(channel_id);
1485 COMMAND_REPLY_ERROR;
1489 /* Get the ban list */
1490 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1492 /* Notify application */
1493 COMMAND_REPLY((ARGS, channel, tmp));
1496 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1497 silc_client_command_reply_free(cmd);
1500 /* Reply to LEAVE command. */
1502 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1504 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1505 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1506 SilcChannelID *channel_id;
1507 SilcChannelEntry channel = NULL;
1511 if (cmd->error != SILC_STATUS_OK) {
1512 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1513 "%s", silc_get_status_message(cmd->error));
1514 COMMAND_REPLY_ERROR;
1518 /* From protocol version 1.1 we get the channel ID of the left channel */
1519 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1521 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1525 /* Get the channel entry */
1526 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1528 silc_free(channel_id);
1529 COMMAND_REPLY_ERROR;
1533 silc_free(channel_id);
1536 /* Notify application */
1537 COMMAND_REPLY((ARGS, channel));
1540 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1541 silc_client_command_reply_free(cmd);
1544 /* Channel resolving callback for USERS command reply. */
1546 static void silc_client_command_reply_users_cb(SilcClient client,
1547 SilcClientConnection conn,
1548 SilcChannelEntry *channels,
1549 SilcUInt32 channels_count,
1552 if (!channels_count) {
1553 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1554 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1556 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1557 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1558 "%s", silc_get_status_message(cmd->error));
1559 COMMAND_REPLY_ERROR;
1560 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1561 silc_client_command_reply_free(cmd);
1565 silc_client_command_reply_users(context, NULL);
1569 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1572 SilcGetChannelCallback get_channel,
1573 SilcCommandCb get_clients)
1575 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1576 SilcChannelEntry channel;
1577 SilcClientEntry client_entry;
1578 SilcChannelUser chu;
1579 SilcChannelID *channel_id = NULL;
1580 SilcBufferStruct client_id_list, client_mode_list;
1582 SilcUInt32 tmp_len, list_count;
1584 unsigned char **res_argv = NULL;
1585 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1586 bool wait_res = FALSE;
1588 SILC_LOG_DEBUG(("Start"));
1590 /* Get channel ID */
1591 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1593 COMMAND_REPLY_ERROR;
1596 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1598 COMMAND_REPLY_ERROR;
1602 /* Get the list count */
1603 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1605 COMMAND_REPLY_ERROR;
1608 SILC_GET32_MSB(list_count, tmp);
1610 /* Get Client ID list */
1611 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1613 COMMAND_REPLY_ERROR;
1616 silc_buffer_set(&client_id_list, tmp, tmp_len);
1618 /* Get client mode list */
1619 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1621 COMMAND_REPLY_ERROR;
1624 silc_buffer_set(&client_mode_list, tmp, tmp_len);
1626 /* Get channel entry */
1627 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1629 /* Resolve the channel from server */
1630 silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1632 silc_free(channel_id);
1636 SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1638 /* Cache the received Client ID's and modes. */
1639 for (i = 0; i < list_count; i++) {
1642 SilcClientID *client_id;
1645 SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1647 client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1652 SILC_GET32_MSB(mode, client_mode_list.data);
1654 /* Check if we have this client cached already. */
1655 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1656 if (!client_entry || !client_entry->username || !client_entry->realname) {
1657 /* No we don't have it (or it is incomplete in information), query
1658 it from the server. Assemble argument table that will be sent
1659 for the WHOIS command later. */
1660 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1662 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1664 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1666 res_argv[res_argc] = client_id_list.data;
1667 res_argv_lens[res_argc] = idp_len;
1668 res_argv_types[res_argc] = res_argc + 4;
1671 if (!silc_client_on_channel(channel, client_entry)) {
1672 chu = silc_calloc(1, sizeof(*chu));
1673 chu->client = client_entry;
1675 chu->channel = channel;
1676 silc_hash_table_add(channel->user_list, client_entry, chu);
1677 silc_hash_table_add(client_entry->channels, channel, chu);
1681 silc_free(client_id);
1682 silc_buffer_pull(&client_id_list, idp_len);
1683 silc_buffer_pull(&client_mode_list, 4);
1686 /* Query the client information from server if the list included clients
1687 that we don't know about. */
1691 /* Send the WHOIS command to server */
1692 silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1693 silc_client_command_reply_whois_i, 0,
1695 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1696 res_argc, res_argv, res_argv_lens,
1697 res_argv_types, conn->cmd_ident);
1698 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1699 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1702 /* Register pending command callback. After we've received the WHOIS
1703 command reply we will reprocess this command reply by re-calling this
1704 USERS command reply callback. */
1705 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1708 silc_buffer_free(res_cmd);
1709 silc_free(channel_id);
1710 silc_free(res_argv);
1711 silc_free(res_argv_lens);
1712 silc_free(res_argv_types);
1719 silc_buffer_push(&client_id_list, (client_id_list.data -
1720 client_id_list.head));
1721 silc_buffer_push(&client_mode_list, (client_mode_list.data -
1722 client_mode_list.head));
1724 /* Notify application */
1726 COMMAND_REPLY((ARGS, channel, list_count, &client_id_list,
1727 &client_mode_list));
1730 silc_free(channel_id);
1734 /* Reply to USERS command. Received list of client ID's and theirs modes
1735 on the channel we requested. */
1737 SILC_CLIENT_CMD_REPLY_FUNC(users)
1739 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1740 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1742 SILC_LOG_DEBUG(("Start"));
1744 if (cmd->error != SILC_STATUS_OK) {
1745 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1746 "%s", silc_get_status_message(cmd->error));
1747 COMMAND_REPLY_ERROR;
1751 if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE,
1752 silc_client_command_reply_users_cb,
1753 silc_client_command_reply_users))
1757 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1758 silc_client_command_reply_free(cmd);
1761 /* Received command reply to GETKEY command. WE've received the remote
1762 client's public key. */
1764 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1766 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1767 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1768 SilcIDPayload idp = NULL;
1769 SilcClientID *client_id = NULL;
1770 SilcClientEntry client_entry;
1771 SilcServerID *server_id = NULL;
1772 SilcServerEntry server_entry;
1774 unsigned char *tmp, *pk;
1778 SilcPublicKey public_key = NULL;
1780 SILC_LOG_DEBUG(("Start"));
1782 if (cmd->error != SILC_STATUS_OK) {
1783 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1784 "%s", silc_get_status_message(cmd->error));
1785 COMMAND_REPLY_ERROR;
1789 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1791 COMMAND_REPLY_ERROR;
1794 idp = silc_id_payload_parse(tmp, len);
1796 COMMAND_REPLY_ERROR;
1800 /* Get the public key payload */
1801 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1803 /* Decode the public key */
1804 SILC_GET16_MSB(pk_len, tmp);
1805 SILC_GET16_MSB(type, tmp + 2);
1808 if (type == SILC_SKE_PK_TYPE_SILC)
1809 if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1813 id_type = silc_id_payload_get_type(idp);
1814 if (id_type == SILC_ID_CLIENT) {
1815 /* Received client's public key */
1816 client_id = silc_id_payload_get_id(idp);
1817 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1818 if (!client_entry) {
1819 COMMAND_REPLY_ERROR;
1823 /* Notify application */
1824 COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1825 } else if (id_type == SILC_ID_SERVER) {
1826 /* Received server's public key */
1827 server_id = silc_id_payload_get_id(idp);
1828 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1829 if (!server_entry) {
1830 COMMAND_REPLY_ERROR;
1834 /* Notify application */
1835 COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
1839 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1841 silc_id_payload_free(idp);
1843 silc_pkcs_public_key_free(public_key);
1844 silc_free(client_id);
1845 silc_free(server_id);
1846 silc_client_command_reply_free(cmd);
1849 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1851 silc_client_command_reply_free(context);
1855 /******************************************************************************
1857 Internal command reply functions
1859 ******************************************************************************/
1861 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1863 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1864 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1866 COMMAND_CHECK_STATUS_I;
1868 /* Save WHOIS info */
1869 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1871 /* Pending callbacks are not executed if this was an list entry */
1872 if (cmd->status != SILC_STATUS_OK &&
1873 cmd->status != SILC_STATUS_LIST_END) {
1874 silc_client_command_reply_free(cmd);
1879 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1882 /* If we received notify for invalid ID we'll remove the ID if we
1884 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1885 SilcClientEntry client_entry;
1887 unsigned char *tmp =
1888 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1891 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1893 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1896 silc_client_del_client(cmd->client, conn, client_entry);
1897 silc_free(client_id);
1902 /* Unregister this command reply */
1903 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1904 NULL, silc_client_command_reply_whois_i,
1907 silc_client_command_reply_free(cmd);
1910 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1912 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1913 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1915 COMMAND_CHECK_STATUS_I;
1917 /* Save IDENTIFY info */
1918 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1920 /* Pending callbacks are not executed if this was an list entry */
1921 if (cmd->status != SILC_STATUS_OK &&
1922 cmd->status != SILC_STATUS_LIST_END) {
1923 silc_client_command_reply_free(cmd);
1928 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1931 /* If we received notify for invalid ID we'll remove the ID if we
1933 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1934 SilcClientEntry client_entry;
1936 unsigned char *tmp =
1937 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1940 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1942 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1945 silc_client_del_client(cmd->client, conn, client_entry);
1946 silc_free(client_id);
1951 /* Unregister this command reply */
1952 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1953 NULL, silc_client_command_reply_identify_i,
1956 silc_client_command_reply_free(cmd);
1959 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
1961 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1962 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1964 SilcServerEntry server;
1965 SilcServerID *server_id = NULL;
1966 char *server_name, *server_info;
1969 COMMAND_CHECK_STATUS_I;
1972 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1976 server_id = silc_id_payload_parse_id(tmp, len, NULL);
1980 /* Get server name */
1981 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1985 /* Get server info */
1986 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
1990 /* See whether we have this server cached. If not create it. */
1991 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
1993 SILC_LOG_DEBUG(("New server entry"));
1994 silc_client_add_server(cmd->client, conn, server_name, server_info,
1995 silc_id_dup(server_id, SILC_ID_SERVER));
1999 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2000 silc_free(server_id);
2002 silc_client_command_reply_free(cmd);
2005 static void silc_client_command_reply_users_i_cb(SilcClient client,
2006 SilcClientConnection conn,
2007 SilcChannelEntry *channels,
2008 SilcUInt32 channels_count,
2011 if (!channels_count) {
2012 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2013 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2015 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2016 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2017 "%s", silc_get_status_message(cmd->error));
2018 COMMAND_REPLY_ERROR;
2019 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2020 silc_client_command_reply_free(cmd);
2024 silc_client_command_reply_users_i(context, NULL);
2027 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2029 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2031 COMMAND_CHECK_STATUS_I;
2033 /* Save USERS info */
2034 if (silc_client_command_reply_users_save(
2035 cmd, cmd->status, FALSE,
2036 silc_client_command_reply_users_i_cb,
2037 silc_client_command_reply_users_i))
2041 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2044 /* Unregister this command reply */
2045 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2046 NULL, silc_client_command_reply_users_i,
2049 silc_client_command_reply_free(cmd);
2052 /* Private range commands, specific to this implementation (and compatible
2053 with SILC Server >= 0.9). */
2055 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2057 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2058 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2060 if (cmd->error != SILC_STATUS_OK) {
2061 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2062 "%s", silc_get_status_message(cmd->error));
2063 COMMAND_REPLY_ERROR;
2067 /* Notify application */
2068 COMMAND_REPLY((ARGS));
2071 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2072 silc_client_command_reply_free(cmd);
2075 SILC_CLIENT_CMD_REPLY_FUNC(close)
2077 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2078 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2080 if (cmd->error != SILC_STATUS_OK) {
2081 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2082 "%s", silc_get_status_message(cmd->error));
2083 COMMAND_REPLY_ERROR;
2087 /* Notify application */
2088 COMMAND_REPLY((ARGS));
2091 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2092 silc_client_command_reply_free(cmd);
2095 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2097 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2098 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2100 if (cmd->error != SILC_STATUS_OK) {
2101 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2102 "%s", silc_get_status_message(cmd->error));
2103 COMMAND_REPLY_ERROR;
2107 /* Notify application */
2108 COMMAND_REPLY((ARGS));
2111 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2112 silc_client_command_reply_free(cmd);