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((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;
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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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((SILC_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;
1776 SilcPublicKey public_key = NULL;
1778 SILC_LOG_DEBUG(("Start"));
1780 if (cmd->error != SILC_STATUS_OK) {
1781 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1782 "%s", silc_get_status_message(cmd->error));
1783 COMMAND_REPLY_ERROR;
1787 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1789 COMMAND_REPLY_ERROR;
1792 idp = silc_id_payload_parse(tmp, len);
1794 COMMAND_REPLY_ERROR;
1798 /* Get the public key payload */
1799 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1801 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1805 id_type = silc_id_payload_get_type(idp);
1806 if (id_type == SILC_ID_CLIENT) {
1807 /* Received client's public key */
1808 client_id = silc_id_payload_get_id(idp);
1809 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1810 if (!client_entry) {
1811 COMMAND_REPLY_ERROR;
1815 /* Notify application */
1816 COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key));
1817 } else if (id_type == SILC_ID_SERVER) {
1818 /* Received server's public key */
1819 server_id = silc_id_payload_get_id(idp);
1820 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1821 if (!server_entry) {
1822 COMMAND_REPLY_ERROR;
1826 /* Notify application */
1827 COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1831 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1833 silc_id_payload_free(idp);
1835 silc_pkcs_public_key_free(public_key);
1836 silc_free(client_id);
1837 silc_free(server_id);
1838 silc_client_command_reply_free(cmd);
1841 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1843 silc_client_command_reply_free(context);
1847 /******************************************************************************
1849 Internal command reply functions
1851 ******************************************************************************/
1853 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1855 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1856 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1858 COMMAND_CHECK_STATUS_I;
1860 /* Save WHOIS info */
1861 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1863 /* Pending callbacks are not executed if this was an list entry */
1864 if (cmd->status != SILC_STATUS_OK &&
1865 cmd->status != SILC_STATUS_LIST_END) {
1866 silc_client_command_reply_free(cmd);
1871 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1874 /* If we received notify for invalid ID we'll remove the ID if we
1876 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1877 SilcClientEntry client_entry;
1879 unsigned char *tmp =
1880 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1883 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1885 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1888 silc_client_del_client(cmd->client, conn, client_entry);
1889 silc_free(client_id);
1894 /* Unregister this command reply */
1895 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1896 NULL, silc_client_command_reply_whois_i,
1899 silc_client_command_reply_free(cmd);
1902 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1904 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1905 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1907 COMMAND_CHECK_STATUS_I;
1909 /* Save IDENTIFY info */
1910 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1912 /* Pending callbacks are not executed if this was an list entry */
1913 if (cmd->status != SILC_STATUS_OK &&
1914 cmd->status != SILC_STATUS_LIST_END) {
1915 silc_client_command_reply_free(cmd);
1920 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1923 /* If we received notify for invalid ID we'll remove the ID if we
1925 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1926 SilcClientEntry client_entry;
1928 unsigned char *tmp =
1929 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1932 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1934 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1937 silc_client_del_client(cmd->client, conn, client_entry);
1938 silc_free(client_id);
1943 /* Unregister this command reply */
1944 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1945 NULL, silc_client_command_reply_identify_i,
1948 silc_client_command_reply_free(cmd);
1951 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
1953 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1954 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1956 SilcServerEntry server;
1957 SilcServerID *server_id = NULL;
1958 char *server_name, *server_info;
1961 COMMAND_CHECK_STATUS_I;
1964 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1968 server_id = silc_id_payload_parse_id(tmp, len, NULL);
1972 /* Get server name */
1973 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1977 /* Get server info */
1978 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
1982 /* See whether we have this server cached. If not create it. */
1983 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
1985 SILC_LOG_DEBUG(("New server entry"));
1986 silc_client_add_server(cmd->client, conn, server_name, server_info,
1987 silc_id_dup(server_id, SILC_ID_SERVER));
1991 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
1992 silc_free(server_id);
1994 silc_client_command_reply_free(cmd);
1997 static void silc_client_command_reply_users_i_cb(SilcClient client,
1998 SilcClientConnection conn,
1999 SilcChannelEntry *channels,
2000 SilcUInt32 channels_count,
2003 if (!channels_count) {
2004 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2005 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2007 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2008 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2009 "%s", silc_get_status_message(cmd->error));
2010 COMMAND_REPLY_ERROR;
2011 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2012 silc_client_command_reply_free(cmd);
2016 silc_client_command_reply_users_i(context, NULL);
2019 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2021 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2023 COMMAND_CHECK_STATUS_I;
2025 /* Save USERS info */
2026 if (silc_client_command_reply_users_save(
2027 cmd, cmd->status, FALSE,
2028 silc_client_command_reply_users_i_cb,
2029 silc_client_command_reply_users_i))
2033 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2036 /* Unregister this command reply */
2037 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2038 NULL, silc_client_command_reply_users_i,
2041 silc_client_command_reply_free(cmd);
2044 /* Private range commands, specific to this implementation (and compatible
2045 with SILC Server >= 0.9). */
2047 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2049 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2050 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2052 if (cmd->error != SILC_STATUS_OK) {
2053 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2054 "%s", silc_get_status_message(cmd->error));
2055 COMMAND_REPLY_ERROR;
2059 /* Notify application */
2060 COMMAND_REPLY((SILC_ARGS));
2063 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2064 silc_client_command_reply_free(cmd);
2067 SILC_CLIENT_CMD_REPLY_FUNC(close)
2069 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2070 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2072 if (cmd->error != SILC_STATUS_OK) {
2073 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2074 "%s", silc_get_status_message(cmd->error));
2075 COMMAND_REPLY_ERROR;
2079 /* Notify application */
2080 COMMAND_REPLY((SILC_ARGS));
2083 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2084 silc_client_command_reply_free(cmd);
2087 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2089 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2090 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2092 if (cmd->error != SILC_STATUS_OK) {
2093 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2094 "%s", silc_get_status_message(cmd->error));
2095 COMMAND_REPLY_ERROR;
2099 /* Notify application */
2100 COMMAND_REPLY((SILC_ARGS));
2103 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2104 silc_client_command_reply_free(cmd);