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;
946 SILC_LOG_DEBUG(("Start"));
948 if (cmd->error != SILC_STATUS_OK) {
949 if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL)
950 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
951 "%s", silc_get_status_message(cmd->error));
956 argc = silc_argument_get_arg_num(cmd->args);
958 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
959 "Cannot join channel: Bad reply packet");
964 /* Get channel name */
965 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
967 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
968 "Cannot join channel: Bad reply packet");
975 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
977 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
978 "Cannot join channel: Bad reply packet");
982 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
988 /* Get channel mode */
989 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
991 SILC_GET32_MSB(mode, tmp);
993 /* Get channel key */
994 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
996 keyp = silc_buffer_alloc(len);
997 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
998 silc_buffer_put(keyp, tmp, len);
1002 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1004 /* Check whether we have this channel entry already. */
1005 channel = silc_client_get_channel(cmd->client, conn, channel_name);
1007 if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
1008 silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
1010 /* Create new channel entry */
1011 channel = silc_client_add_channel(cmd->client, conn, channel_name,
1015 conn->current_channel = channel;
1018 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1020 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1021 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1022 "Cannot join channel: Unsupported HMAC `%s'", hmac);
1023 COMMAND_REPLY_ERROR;
1028 /* Get the list count */
1029 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1032 SILC_GET32_MSB(list_count, tmp);
1034 /* Get Client ID list */
1035 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1039 client_id_list = silc_buffer_alloc(len);
1040 silc_buffer_pull_tail(client_id_list, len);
1041 silc_buffer_put(client_id_list, tmp, len);
1043 /* Get client mode list */
1044 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1048 client_mode_list = silc_buffer_alloc(len);
1049 silc_buffer_pull_tail(client_mode_list, len);
1050 silc_buffer_put(client_mode_list, tmp, len);
1052 /* Add clients we received in the reply to the channel */
1053 for (i = 0; i < list_count; i++) {
1056 SilcClientID *client_id;
1057 SilcClientEntry client_entry;
1060 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1062 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL);
1067 SILC_GET32_MSB(mode, client_mode_list->data);
1069 /* Check if we have this client cached already. */
1070 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1071 if (!client_entry) {
1072 /* No, we don't have it, add entry for it. */
1074 silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
1075 silc_id_dup(client_id, SILC_ID_CLIENT), 0);
1078 /* Join client to the channel */
1079 if (!silc_client_on_channel(channel, client_entry)) {
1080 chu = silc_calloc(1, sizeof(*chu));
1081 chu->client = client_entry;
1082 chu->channel = channel;
1084 silc_hash_table_add(channel->user_list, client_entry, chu);
1085 silc_hash_table_add(client_entry->channels, channel, chu);
1088 silc_free(client_id);
1089 silc_buffer_pull(client_id_list, idp_len);
1090 silc_buffer_pull(client_mode_list, 4);
1092 silc_buffer_push(client_id_list, client_id_list->data -
1093 client_id_list->head);
1094 silc_buffer_push(client_mode_list, client_mode_list->data -
1095 client_mode_list->head);
1097 /* Save channel key */
1098 if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1099 silc_client_save_channel_key(cmd->client, conn, keyp, channel);
1101 /* Notify application */
1102 COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0,
1103 keyp ? keyp->head : NULL, NULL,
1104 NULL, topic, hmac, list_count, client_id_list,
1108 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1109 silc_client_command_reply_free(cmd);
1112 silc_buffer_free(keyp);
1114 silc_buffer_free(client_id_list);
1115 if (client_mode_list)
1116 silc_buffer_free(client_mode_list);
1119 /* Received reply for MOTD command */
1121 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1123 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1124 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1126 char *motd = NULL, *cp, line[256];
1128 if (cmd->error != SILC_STATUS_OK) {
1129 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1130 "%s", silc_get_status_message(cmd->error));
1131 COMMAND_REPLY_ERROR;
1135 argc = silc_argument_get_arg_num(cmd->args);
1137 COMMAND_REPLY_ERROR;
1142 motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1144 COMMAND_REPLY_ERROR;
1151 if (cp[i++] == '\n') {
1152 memset(line, 0, sizeof(line));
1153 silc_strncat(line, sizeof(line), cp, i - 1);
1159 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1168 /* Notify application */
1169 COMMAND_REPLY((SILC_ARGS, motd));
1172 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1173 silc_client_command_reply_free(cmd);
1176 /* Received reply tot he UMODE command. Save the current user mode */
1178 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1180 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1181 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1185 if (cmd->error != SILC_STATUS_OK) {
1186 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1187 "%s", silc_get_status_message(cmd->error));
1188 COMMAND_REPLY_ERROR;
1192 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1194 COMMAND_REPLY_ERROR;
1198 SILC_GET32_MSB(mode, tmp);
1199 conn->local_entry->mode = mode;
1201 /* Notify application */
1202 COMMAND_REPLY((SILC_ARGS, mode));
1205 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1206 silc_client_command_reply_free(cmd);
1209 /* Received reply for CMODE command. */
1211 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1213 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1214 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1217 SilcChannelID *channel_id;
1218 SilcChannelEntry channel;
1220 SilcPublicKey public_key = NULL;
1221 SilcBufferStruct channel_pubkeys;
1223 if (cmd->error != SILC_STATUS_OK) {
1224 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1225 "%s", silc_get_status_message(cmd->error));
1226 COMMAND_REPLY_ERROR;
1230 /* Take Channel ID */
1231 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1234 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1238 /* Get the channel entry */
1239 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1241 silc_free(channel_id);
1242 COMMAND_REPLY_ERROR;
1246 /* Get channel mode */
1247 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1249 silc_free(channel_id);
1250 COMMAND_REPLY_ERROR;
1255 SILC_GET32_MSB(mode, tmp);
1256 channel->mode = mode;
1258 /* Get founder public key */
1259 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
1261 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1265 /* Get channel public key(s) */
1266 tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
1268 silc_buffer_set(&channel_pubkeys, tmp, len);
1270 /* Notify application */
1271 COMMAND_REPLY((SILC_ARGS, channel, mode, public_key,
1272 tmp ? &channel_pubkeys : NULL));
1274 silc_free(channel_id);
1278 silc_pkcs_public_key_free(public_key);
1279 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1280 silc_client_command_reply_free(cmd);
1283 /* Received reply for CUMODE command */
1285 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1287 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1288 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1289 SilcClientID *client_id;
1290 SilcChannelID *channel_id;
1291 SilcClientEntry client_entry;
1292 SilcChannelEntry channel;
1293 SilcChannelUser chu;
1294 unsigned char *modev, *tmp, *id;
1295 SilcUInt32 len, mode;
1297 if (cmd->error != SILC_STATUS_OK) {
1298 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1299 "%s", silc_get_status_message(cmd->error));
1300 COMMAND_REPLY_ERROR;
1304 /* Get channel mode */
1305 modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1307 COMMAND_REPLY_ERROR;
1311 /* Take Channel ID */
1312 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1315 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1319 /* Get the channel entry */
1320 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1322 silc_free(channel_id);
1323 COMMAND_REPLY_ERROR;
1328 id = silc_argument_get_arg_type(cmd->args, 4, &len);
1330 silc_free(channel_id);
1331 COMMAND_REPLY_ERROR;
1334 client_id = silc_id_payload_parse_id(id, len, NULL);
1336 silc_free(channel_id);
1337 COMMAND_REPLY_ERROR;
1341 /* Get client entry */
1342 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1343 if (!client_entry) {
1344 silc_free(channel_id);
1345 silc_free(client_id);
1346 COMMAND_REPLY_ERROR;
1351 SILC_GET32_MSB(mode, modev);
1352 chu = silc_client_on_channel(channel, client_entry);
1356 /* Notify application */
1357 COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1358 silc_free(client_id);
1359 silc_free(channel_id);
1362 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1363 silc_client_command_reply_free(cmd);
1366 SILC_CLIENT_CMD_REPLY_FUNC(kick)
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((SILC_ARGS));
1382 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1383 silc_client_command_reply_free(cmd);
1386 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
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((SILC_ARGS));
1402 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1403 silc_client_command_reply_free(cmd);
1406 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1408 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1409 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1411 if (cmd->error != SILC_STATUS_OK) {
1412 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1413 "%s", silc_get_status_message(cmd->error));
1414 COMMAND_REPLY_ERROR;
1418 /* Notify application */
1419 COMMAND_REPLY((SILC_ARGS));
1422 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1423 silc_client_command_reply_free(cmd);
1426 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1428 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1429 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1432 if (cmd->error != SILC_STATUS_OK) {
1433 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1434 "%s", silc_get_status_message(cmd->error));
1435 COMMAND_REPLY_ERROR;
1439 /* Notify application */
1440 COMMAND_REPLY((SILC_ARGS));
1442 /* Generate the detachment data and deliver it to the client in the
1443 detach client operation */
1444 detach = silc_client_get_detach_data(cmd->client, conn);
1446 cmd->client->internal->ops->detach(cmd->client, conn,
1447 detach->data, detach->len);
1448 silc_buffer_free(detach);
1452 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1453 silc_client_command_reply_free(cmd);
1456 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1458 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1459 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1461 if (cmd->error != SILC_STATUS_OK) {
1462 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1463 "%s", silc_get_status_message(cmd->error));
1464 COMMAND_REPLY_ERROR;
1468 /* Notify application */
1469 COMMAND_REPLY((SILC_ARGS));
1472 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1473 silc_client_command_reply_free(cmd);
1476 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1478 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1479 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1480 SilcChannelEntry channel;
1481 SilcChannelID *channel_id;
1484 SilcBufferStruct buf;
1486 if (cmd->error != SILC_STATUS_OK) {
1487 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1488 "%s", silc_get_status_message(cmd->error));
1489 COMMAND_REPLY_ERROR;
1493 /* Take Channel ID */
1494 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1498 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1502 /* Get the channel entry */
1503 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1505 silc_free(channel_id);
1506 COMMAND_REPLY_ERROR;
1510 /* Get the ban list */
1511 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1513 silc_buffer_set(&buf, tmp, len);
1515 /* Notify application */
1516 COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1519 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1520 silc_client_command_reply_free(cmd);
1523 /* Reply to LEAVE command. */
1525 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1527 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1528 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1529 SilcChannelID *channel_id;
1530 SilcChannelEntry channel = NULL;
1534 if (cmd->error != SILC_STATUS_OK) {
1535 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1536 "%s", silc_get_status_message(cmd->error));
1537 COMMAND_REPLY_ERROR;
1541 /* From protocol version 1.1 we get the channel ID of the left channel */
1542 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1544 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1548 /* Get the channel entry */
1549 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1551 silc_free(channel_id);
1552 COMMAND_REPLY_ERROR;
1556 silc_free(channel_id);
1559 /* Notify application */
1560 COMMAND_REPLY((SILC_ARGS, channel));
1563 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1564 silc_client_command_reply_free(cmd);
1567 /* Channel resolving callback for USERS command reply. */
1569 static void silc_client_command_reply_users_cb(SilcClient client,
1570 SilcClientConnection conn,
1571 SilcChannelEntry *channels,
1572 SilcUInt32 channels_count,
1575 if (!channels_count) {
1576 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1577 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1579 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1580 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1581 "%s", silc_get_status_message(cmd->error));
1582 COMMAND_REPLY_ERROR;
1583 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1584 silc_client_command_reply_free(cmd);
1588 silc_client_command_reply_users(context, NULL);
1592 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1596 SilcGetChannelCallback get_channel,
1597 SilcCommandCb get_clients)
1599 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1600 SilcChannelEntry channel;
1601 SilcClientEntry client_entry;
1602 SilcChannelUser chu;
1603 SilcChannelID *channel_id = NULL;
1604 SilcBufferStruct client_id_list, client_mode_list;
1606 SilcUInt32 tmp_len, list_count;
1608 unsigned char **res_argv = NULL;
1609 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1610 bool wait_res = FALSE;
1612 SILC_LOG_DEBUG(("Start"));
1614 /* Get channel ID */
1615 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1617 COMMAND_REPLY_ERROR;
1620 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1622 COMMAND_REPLY_ERROR;
1626 /* Get the list count */
1627 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1629 COMMAND_REPLY_ERROR;
1632 SILC_GET32_MSB(list_count, tmp);
1634 /* Get Client ID list */
1635 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1637 COMMAND_REPLY_ERROR;
1640 silc_buffer_set(&client_id_list, tmp, tmp_len);
1642 /* Get client mode list */
1643 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1645 COMMAND_REPLY_ERROR;
1648 silc_buffer_set(&client_mode_list, tmp, tmp_len);
1650 /* Get channel entry */
1651 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1653 /* Resolve the channel from server */
1654 silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1656 silc_free(channel_id);
1660 SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1662 /* Cache the received Client ID's and modes. */
1663 for (i = 0; i < list_count; i++) {
1666 SilcClientID *client_id;
1669 SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1671 client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1676 SILC_GET32_MSB(mode, client_mode_list.data);
1678 /* Check if we have this client cached already. */
1679 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1680 if (!client_entry || !client_entry->username || !client_entry->realname) {
1682 /* No we don't have it (or it is incomplete in information), query
1683 it from the server. Assemble argument table that will be sent
1684 for the WHOIS command later. */
1685 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1687 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1689 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1691 res_argv[res_argc] = client_id_list.data;
1692 res_argv_lens[res_argc] = idp_len;
1693 res_argv_types[res_argc] = res_argc + 4;
1697 if (!silc_client_on_channel(channel, client_entry)) {
1698 chu = silc_calloc(1, sizeof(*chu));
1699 chu->client = client_entry;
1701 chu->channel = channel;
1702 silc_hash_table_add(channel->user_list, client_entry, chu);
1703 silc_hash_table_add(client_entry->channels, channel, chu);
1707 silc_free(client_id);
1708 silc_buffer_pull(&client_id_list, idp_len);
1709 silc_buffer_pull(&client_mode_list, 4);
1712 /* Query the client information from server if the list included clients
1713 that we don't know about. */
1717 /* Send the WHOIS command to server */
1718 silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1719 silc_client_command_reply_whois_i, 0,
1721 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1722 res_argc, res_argv, res_argv_lens,
1723 res_argv_types, conn->cmd_ident);
1724 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1725 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1728 /* Register pending command callback. After we've received the WHOIS
1729 command reply we will reprocess this command reply by re-calling this
1730 USERS command reply callback. */
1731 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1734 silc_buffer_free(res_cmd);
1735 silc_free(channel_id);
1736 silc_free(res_argv);
1737 silc_free(res_argv_lens);
1738 silc_free(res_argv_types);
1745 silc_buffer_push(&client_id_list, (client_id_list.data -
1746 client_id_list.head));
1747 silc_buffer_push(&client_mode_list, (client_mode_list.data -
1748 client_mode_list.head));
1750 /* Notify application */
1752 COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1753 &client_mode_list));
1756 silc_free(channel_id);
1760 /* Reply to USERS command. Received list of client ID's and theirs modes
1761 on the channel we requested. */
1763 SILC_CLIENT_CMD_REPLY_FUNC(users)
1765 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1766 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1767 SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
1769 SILC_LOG_DEBUG(("Start"));
1771 if (cmd->error != SILC_STATUS_OK) {
1772 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1773 "%s", silc_get_status_message(cmd->error));
1774 COMMAND_REPLY_ERROR;
1778 if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1779 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1780 /* Do not resolve anymore. Server may be sending us some non-existent
1781 Client ID (a bug in server), and we want to show the users list
1783 silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE,
1784 silc_client_command_reply_users_cb,
1785 silc_client_command_reply_users);
1788 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1789 "%s", silc_get_status_message(cmd->error));
1790 COMMAND_REPLY_ERROR;
1795 if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE,
1796 silc_client_command_reply_users_cb,
1797 silc_client_command_reply_users))
1801 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1802 silc_client_command_reply_free(cmd);
1805 /* Received command reply to GETKEY command. WE've received the remote
1806 client's public key. */
1808 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1810 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1811 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1812 SilcIDPayload idp = NULL;
1813 SilcClientID *client_id = NULL;
1814 SilcClientEntry client_entry;
1815 SilcServerID *server_id = NULL;
1816 SilcServerEntry server_entry;
1820 SilcPublicKey public_key = NULL;
1822 SILC_LOG_DEBUG(("Start"));
1824 if (cmd->error != SILC_STATUS_OK) {
1825 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1826 "%s", silc_get_status_message(cmd->error));
1827 COMMAND_REPLY_ERROR;
1831 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1833 COMMAND_REPLY_ERROR;
1836 idp = silc_id_payload_parse(tmp, len);
1838 COMMAND_REPLY_ERROR;
1842 /* Get the public key payload */
1843 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1845 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1849 id_type = silc_id_payload_get_type(idp);
1850 if (id_type == SILC_ID_CLIENT) {
1851 /* Received client's public key */
1852 client_id = silc_id_payload_get_id(idp);
1853 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1854 if (!client_entry) {
1855 COMMAND_REPLY_ERROR;
1859 /* Save fingerprint */
1860 if (!client_entry->fingerprint) {
1861 client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1862 client_entry->fingerprint_len = 20;
1863 silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1864 client_entry->fingerprint);
1867 /* Notify application */
1868 COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key));
1869 } else if (id_type == SILC_ID_SERVER) {
1870 /* Received server's public key */
1871 server_id = silc_id_payload_get_id(idp);
1872 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1873 if (!server_entry) {
1874 COMMAND_REPLY_ERROR;
1878 /* Notify application */
1879 COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1883 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1885 silc_id_payload_free(idp);
1887 silc_pkcs_public_key_free(public_key);
1888 silc_free(client_id);
1889 silc_free(server_id);
1890 silc_client_command_reply_free(cmd);
1893 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1895 silc_client_command_reply_free(context);
1899 /******************************************************************************
1901 Internal command reply functions
1903 ******************************************************************************/
1905 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1907 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1908 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1910 COMMAND_CHECK_STATUS_I;
1912 /* Save WHOIS info */
1913 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1915 /* Pending callbacks are not executed if this was an list entry */
1916 if (cmd->status != SILC_STATUS_OK &&
1917 cmd->status != SILC_STATUS_LIST_END) {
1918 silc_client_command_reply_free(cmd);
1923 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1926 /* If we received notify for invalid ID we'll remove the ID if we
1928 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1929 SilcClientEntry client_entry;
1931 unsigned char *tmp =
1932 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1935 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1937 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1940 silc_client_del_client(cmd->client, conn, client_entry);
1941 silc_free(client_id);
1946 /* Unregister this command reply */
1947 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1948 NULL, silc_client_command_reply_whois_i,
1951 silc_client_command_reply_free(cmd);
1954 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1956 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1957 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1959 COMMAND_CHECK_STATUS_I;
1961 /* Save IDENTIFY info */
1962 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1964 /* Pending callbacks are not executed if this was an list entry */
1965 if (cmd->status != SILC_STATUS_OK &&
1966 cmd->status != SILC_STATUS_LIST_END) {
1967 silc_client_command_reply_free(cmd);
1972 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1975 /* If we received notify for invalid ID we'll remove the ID if we
1977 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1978 SilcClientEntry client_entry;
1980 unsigned char *tmp =
1981 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1984 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1986 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1989 silc_client_del_client(cmd->client, conn, client_entry);
1990 silc_free(client_id);
1995 /* Unregister this command reply */
1996 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1997 NULL, silc_client_command_reply_identify_i,
2000 silc_client_command_reply_free(cmd);
2003 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
2005 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2006 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2008 SilcServerEntry server;
2009 SilcServerID *server_id = NULL;
2010 char *server_name, *server_info;
2013 COMMAND_CHECK_STATUS_I;
2016 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
2020 server_id = silc_id_payload_parse_id(tmp, len, NULL);
2024 /* Get server name */
2025 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
2029 /* Get server info */
2030 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2034 /* See whether we have this server cached. If not create it. */
2035 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2037 SILC_LOG_DEBUG(("New server entry"));
2038 silc_client_add_server(cmd->client, conn, server_name, server_info,
2039 silc_id_dup(server_id, SILC_ID_SERVER));
2043 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2044 silc_free(server_id);
2046 silc_client_command_reply_free(cmd);
2049 static void silc_client_command_reply_users_i_cb(SilcClient client,
2050 SilcClientConnection conn,
2051 SilcChannelEntry *channels,
2052 SilcUInt32 channels_count,
2055 if (!channels_count) {
2056 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2057 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2059 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2060 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2061 "%s", silc_get_status_message(cmd->error));
2062 COMMAND_REPLY_ERROR;
2063 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2064 silc_client_command_reply_free(cmd);
2068 silc_client_command_reply_users_i(context, NULL);
2071 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2073 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2075 COMMAND_CHECK_STATUS_I;
2077 /* Save USERS info */
2078 if (silc_client_command_reply_users_save(
2079 cmd, cmd->status, FALSE, TRUE,
2080 silc_client_command_reply_users_i_cb,
2081 silc_client_command_reply_users_i))
2085 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2088 /* Unregister this command reply */
2089 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2090 NULL, silc_client_command_reply_users_i,
2093 silc_client_command_reply_free(cmd);
2096 /* Private range commands, specific to this implementation (and compatible
2097 with SILC Server >= 0.9). */
2099 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2101 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2102 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2104 if (cmd->error != SILC_STATUS_OK) {
2105 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2106 "%s", silc_get_status_message(cmd->error));
2107 COMMAND_REPLY_ERROR;
2111 /* Notify application */
2112 COMMAND_REPLY((SILC_ARGS));
2115 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2116 silc_client_command_reply_free(cmd);
2119 SILC_CLIENT_CMD_REPLY_FUNC(close)
2121 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2122 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2124 if (cmd->error != SILC_STATUS_OK) {
2125 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2126 "%s", silc_get_status_message(cmd->error));
2127 COMMAND_REPLY_ERROR;
2131 /* Notify application */
2132 COMMAND_REPLY((SILC_ARGS));
2135 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2136 silc_client_command_reply_free(cmd);
2139 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2141 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2142 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2144 if (cmd->error != SILC_STATUS_OK) {
2145 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2146 "%s", silc_get_status_message(cmd->error));
2147 COMMAND_REPLY_ERROR;
2151 /* Notify application */
2152 COMMAND_REPLY((SILC_ARGS));
2155 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2156 silc_client_command_reply_free(cmd);