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;
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;
1221 if (cmd->error != SILC_STATUS_OK) {
1222 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1223 "%s", silc_get_status_message(cmd->error));
1224 COMMAND_REPLY_ERROR;
1228 /* Take Channel ID */
1229 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1232 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1236 /* Get the channel entry */
1237 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1239 silc_free(channel_id);
1240 COMMAND_REPLY_ERROR;
1244 /* Get channel mode */
1245 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1247 silc_free(channel_id);
1248 COMMAND_REPLY_ERROR;
1253 SILC_GET32_MSB(mode, tmp);
1254 channel->mode = mode;
1256 /* Notify application */
1257 COMMAND_REPLY((SILC_ARGS, channel, mode));
1259 silc_free(channel_id);
1262 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1263 silc_client_command_reply_free(cmd);
1266 /* Received reply for CUMODE command */
1268 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1270 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1271 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1272 SilcClientID *client_id;
1273 SilcChannelID *channel_id;
1274 SilcClientEntry client_entry;
1275 SilcChannelEntry channel;
1276 SilcChannelUser chu;
1277 unsigned char *modev, *tmp, *id;
1278 SilcUInt32 len, mode;
1280 if (cmd->error != SILC_STATUS_OK) {
1281 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1282 "%s", silc_get_status_message(cmd->error));
1283 COMMAND_REPLY_ERROR;
1287 /* Get channel mode */
1288 modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
1290 COMMAND_REPLY_ERROR;
1294 /* Take Channel ID */
1295 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1298 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1302 /* Get the channel entry */
1303 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1305 silc_free(channel_id);
1306 COMMAND_REPLY_ERROR;
1311 id = silc_argument_get_arg_type(cmd->args, 4, &len);
1313 silc_free(channel_id);
1314 COMMAND_REPLY_ERROR;
1317 client_id = silc_id_payload_parse_id(id, len, NULL);
1319 silc_free(channel_id);
1320 COMMAND_REPLY_ERROR;
1324 /* Get client entry */
1325 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1326 if (!client_entry) {
1327 silc_free(channel_id);
1328 silc_free(client_id);
1329 COMMAND_REPLY_ERROR;
1334 SILC_GET32_MSB(mode, modev);
1335 chu = silc_client_on_channel(channel, client_entry);
1339 /* Notify application */
1340 COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry));
1341 silc_free(client_id);
1342 silc_free(channel_id);
1345 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1346 silc_client_command_reply_free(cmd);
1349 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1351 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1352 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1354 if (cmd->error != SILC_STATUS_OK) {
1355 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1356 "%s", silc_get_status_message(cmd->error));
1357 COMMAND_REPLY_ERROR;
1361 /* Notify application */
1362 COMMAND_REPLY((SILC_ARGS));
1365 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1366 silc_client_command_reply_free(cmd);
1369 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1371 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1372 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1374 if (cmd->error != SILC_STATUS_OK) {
1375 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1376 "%s", silc_get_status_message(cmd->error));
1377 COMMAND_REPLY_ERROR;
1381 /* Notify application */
1382 COMMAND_REPLY((SILC_ARGS));
1385 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1386 silc_client_command_reply_free(cmd);
1389 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1391 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1392 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1394 if (cmd->error != SILC_STATUS_OK) {
1395 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1396 "%s", silc_get_status_message(cmd->error));
1397 COMMAND_REPLY_ERROR;
1401 /* Notify application */
1402 COMMAND_REPLY((SILC_ARGS));
1405 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1406 silc_client_command_reply_free(cmd);
1409 SILC_CLIENT_CMD_REPLY_FUNC(detach)
1411 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1412 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1415 if (cmd->error != SILC_STATUS_OK) {
1416 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1417 "%s", silc_get_status_message(cmd->error));
1418 COMMAND_REPLY_ERROR;
1422 /* Notify application */
1423 COMMAND_REPLY((SILC_ARGS));
1425 /* Generate the detachment data and deliver it to the client in the
1426 detach client operation */
1427 detach = silc_client_get_detach_data(cmd->client, conn);
1429 cmd->client->internal->ops->detach(cmd->client, conn,
1430 detach->data, detach->len);
1431 silc_buffer_free(detach);
1435 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH);
1436 silc_client_command_reply_free(cmd);
1439 SILC_CLIENT_CMD_REPLY_FUNC(watch)
1441 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1442 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1444 if (cmd->error != SILC_STATUS_OK) {
1445 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1446 "%s", silc_get_status_message(cmd->error));
1447 COMMAND_REPLY_ERROR;
1451 /* Notify application */
1452 COMMAND_REPLY((SILC_ARGS));
1455 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1456 silc_client_command_reply_free(cmd);
1459 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1461 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1462 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1463 SilcChannelEntry channel;
1464 SilcChannelID *channel_id;
1467 SilcBufferStruct buf;
1469 if (cmd->error != SILC_STATUS_OK) {
1470 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1471 "%s", silc_get_status_message(cmd->error));
1472 COMMAND_REPLY_ERROR;
1476 /* Take Channel ID */
1477 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1481 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1485 /* Get the channel entry */
1486 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1488 silc_free(channel_id);
1489 COMMAND_REPLY_ERROR;
1493 /* Get the ban list */
1494 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1496 silc_buffer_set(&buf, tmp, len);
1498 /* Notify application */
1499 COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL));
1502 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1503 silc_client_command_reply_free(cmd);
1506 /* Reply to LEAVE command. */
1508 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1510 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1511 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1512 SilcChannelID *channel_id;
1513 SilcChannelEntry channel = NULL;
1517 if (cmd->error != SILC_STATUS_OK) {
1518 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1519 "%s", silc_get_status_message(cmd->error));
1520 COMMAND_REPLY_ERROR;
1524 /* From protocol version 1.1 we get the channel ID of the left channel */
1525 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1527 channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1531 /* Get the channel entry */
1532 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1534 silc_free(channel_id);
1535 COMMAND_REPLY_ERROR;
1539 silc_free(channel_id);
1542 /* Notify application */
1543 COMMAND_REPLY((SILC_ARGS, channel));
1546 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1547 silc_client_command_reply_free(cmd);
1550 /* Channel resolving callback for USERS command reply. */
1552 static void silc_client_command_reply_users_cb(SilcClient client,
1553 SilcClientConnection conn,
1554 SilcChannelEntry *channels,
1555 SilcUInt32 channels_count,
1558 if (!channels_count) {
1559 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1560 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1562 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
1563 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1564 "%s", silc_get_status_message(cmd->error));
1565 COMMAND_REPLY_ERROR;
1566 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1567 silc_client_command_reply_free(cmd);
1571 silc_client_command_reply_users(context, NULL);
1575 silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd,
1578 SilcGetChannelCallback get_channel,
1579 SilcCommandCb get_clients)
1581 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1582 SilcChannelEntry channel;
1583 SilcClientEntry client_entry;
1584 SilcChannelUser chu;
1585 SilcChannelID *channel_id = NULL;
1586 SilcBufferStruct client_id_list, client_mode_list;
1588 SilcUInt32 tmp_len, list_count;
1590 unsigned char **res_argv = NULL;
1591 SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1592 bool wait_res = FALSE;
1594 SILC_LOG_DEBUG(("Start"));
1596 /* Get channel ID */
1597 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1599 COMMAND_REPLY_ERROR;
1602 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1604 COMMAND_REPLY_ERROR;
1608 /* Get the list count */
1609 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1611 COMMAND_REPLY_ERROR;
1614 SILC_GET32_MSB(list_count, tmp);
1616 /* Get Client ID list */
1617 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1619 COMMAND_REPLY_ERROR;
1622 silc_buffer_set(&client_id_list, tmp, tmp_len);
1624 /* Get client mode list */
1625 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1627 COMMAND_REPLY_ERROR;
1630 silc_buffer_set(&client_mode_list, tmp, tmp_len);
1632 /* Get channel entry */
1633 channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
1635 /* Resolve the channel from server */
1636 silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
1638 silc_free(channel_id);
1642 SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1644 /* Cache the received Client ID's and modes. */
1645 for (i = 0; i < list_count; i++) {
1648 SilcClientID *client_id;
1651 SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1653 client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL);
1658 SILC_GET32_MSB(mode, client_mode_list.data);
1660 /* Check if we have this client cached already. */
1661 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1662 if (!client_entry || !client_entry->username || !client_entry->realname) {
1663 /* No we don't have it (or it is incomplete in information), query
1664 it from the server. Assemble argument table that will be sent
1665 for the WHOIS command later. */
1666 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1668 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1670 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1672 res_argv[res_argc] = client_id_list.data;
1673 res_argv_lens[res_argc] = idp_len;
1674 res_argv_types[res_argc] = res_argc + 4;
1677 if (!silc_client_on_channel(channel, client_entry)) {
1678 chu = silc_calloc(1, sizeof(*chu));
1679 chu->client = client_entry;
1681 chu->channel = channel;
1682 silc_hash_table_add(channel->user_list, client_entry, chu);
1683 silc_hash_table_add(client_entry->channels, channel, chu);
1687 silc_free(client_id);
1688 silc_buffer_pull(&client_id_list, idp_len);
1689 silc_buffer_pull(&client_mode_list, 4);
1692 /* Query the client information from server if the list included clients
1693 that we don't know about. */
1697 /* Send the WHOIS command to server */
1698 silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
1699 silc_client_command_reply_whois_i, 0,
1701 res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1702 res_argc, res_argv, res_argv_lens,
1703 res_argv_types, conn->cmd_ident);
1704 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1705 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1708 /* Register pending command callback. After we've received the WHOIS
1709 command reply we will reprocess this command reply by re-calling this
1710 USERS command reply callback. */
1711 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1714 silc_buffer_free(res_cmd);
1715 silc_free(channel_id);
1716 silc_free(res_argv);
1717 silc_free(res_argv_lens);
1718 silc_free(res_argv_types);
1725 silc_buffer_push(&client_id_list, (client_id_list.data -
1726 client_id_list.head));
1727 silc_buffer_push(&client_mode_list, (client_mode_list.data -
1728 client_mode_list.head));
1730 /* Notify application */
1732 COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list,
1733 &client_mode_list));
1736 silc_free(channel_id);
1740 /* Reply to USERS command. Received list of client ID's and theirs modes
1741 on the channel we requested. */
1743 SILC_CLIENT_CMD_REPLY_FUNC(users)
1745 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1746 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1747 SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2;
1749 SILC_LOG_DEBUG(("Start"));
1751 if (cmd->error != SILC_STATUS_OK) {
1752 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1753 "%s", silc_get_status_message(cmd->error));
1754 COMMAND_REPLY_ERROR;
1758 if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) {
1759 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1760 "%s", silc_get_status_message(cmd->error));
1761 COMMAND_REPLY_ERROR;
1765 if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE,
1766 silc_client_command_reply_users_cb,
1767 silc_client_command_reply_users))
1771 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1772 silc_client_command_reply_free(cmd);
1775 /* Received command reply to GETKEY command. WE've received the remote
1776 client's public key. */
1778 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1780 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1781 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1782 SilcIDPayload idp = NULL;
1783 SilcClientID *client_id = NULL;
1784 SilcClientEntry client_entry;
1785 SilcServerID *server_id = NULL;
1786 SilcServerEntry server_entry;
1790 SilcPublicKey public_key = NULL;
1792 SILC_LOG_DEBUG(("Start"));
1794 if (cmd->error != SILC_STATUS_OK) {
1795 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1796 "%s", silc_get_status_message(cmd->error));
1797 COMMAND_REPLY_ERROR;
1801 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1803 COMMAND_REPLY_ERROR;
1806 idp = silc_id_payload_parse(tmp, len);
1808 COMMAND_REPLY_ERROR;
1812 /* Get the public key payload */
1813 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1815 if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key))
1819 id_type = silc_id_payload_get_type(idp);
1820 if (id_type == SILC_ID_CLIENT) {
1821 /* Received client's public key */
1822 client_id = silc_id_payload_get_id(idp);
1823 client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
1824 if (!client_entry) {
1825 COMMAND_REPLY_ERROR;
1829 /* Save fingerprint */
1830 if (!client_entry->fingerprint) {
1831 client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char));
1832 client_entry->fingerprint_len = 20;
1833 silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4,
1834 client_entry->fingerprint);
1837 /* Notify application */
1838 COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key));
1839 } else if (id_type == SILC_ID_SERVER) {
1840 /* Received server's public key */
1841 server_id = silc_id_payload_get_id(idp);
1842 server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id);
1843 if (!server_entry) {
1844 COMMAND_REPLY_ERROR;
1848 /* Notify application */
1849 COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key));
1853 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1855 silc_id_payload_free(idp);
1857 silc_pkcs_public_key_free(public_key);
1858 silc_free(client_id);
1859 silc_free(server_id);
1860 silc_client_command_reply_free(cmd);
1863 SILC_CLIENT_CMD_REPLY_FUNC(quit)
1865 silc_client_command_reply_free(context);
1869 /******************************************************************************
1871 Internal command reply functions
1873 ******************************************************************************/
1875 SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
1877 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1878 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1880 COMMAND_CHECK_STATUS_I;
1882 /* Save WHOIS info */
1883 silc_client_command_reply_whois_save(cmd, cmd->status, FALSE);
1885 /* Pending callbacks are not executed if this was an list entry */
1886 if (cmd->status != SILC_STATUS_OK &&
1887 cmd->status != SILC_STATUS_LIST_END) {
1888 silc_client_command_reply_free(cmd);
1893 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
1896 /* If we received notify for invalid ID we'll remove the ID if we
1898 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1899 SilcClientEntry client_entry;
1901 unsigned char *tmp =
1902 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1905 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1907 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1910 silc_client_del_client(cmd->client, conn, client_entry);
1911 silc_free(client_id);
1916 /* Unregister this command reply */
1917 silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
1918 NULL, silc_client_command_reply_whois_i,
1921 silc_client_command_reply_free(cmd);
1924 SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
1926 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1927 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1929 COMMAND_CHECK_STATUS_I;
1931 /* Save IDENTIFY info */
1932 silc_client_command_reply_identify_save(cmd, cmd->status, FALSE);
1934 /* Pending callbacks are not executed if this was an list entry */
1935 if (cmd->status != SILC_STATUS_OK &&
1936 cmd->status != SILC_STATUS_LIST_END) {
1937 silc_client_command_reply_free(cmd);
1942 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
1945 /* If we received notify for invalid ID we'll remove the ID if we
1947 if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1948 SilcClientEntry client_entry;
1950 unsigned char *tmp =
1951 silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
1954 SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1956 client_entry = silc_client_get_client_by_id(cmd->client, conn,
1959 silc_client_del_client(cmd->client, conn, client_entry);
1960 silc_free(client_id);
1965 /* Unregister this command reply */
1966 silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
1967 NULL, silc_client_command_reply_identify_i,
1970 silc_client_command_reply_free(cmd);
1973 SILC_CLIENT_CMD_REPLY_FUNC(info_i)
1975 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1976 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1978 SilcServerEntry server;
1979 SilcServerID *server_id = NULL;
1980 char *server_name, *server_info;
1983 COMMAND_CHECK_STATUS_I;
1986 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1990 server_id = silc_id_payload_parse_id(tmp, len, NULL);
1994 /* Get server name */
1995 server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1999 /* Get server info */
2000 server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
2004 /* See whether we have this server cached. If not create it. */
2005 server = silc_client_get_server_by_id(cmd->client, conn, server_id);
2007 SILC_LOG_DEBUG(("New server entry"));
2008 silc_client_add_server(cmd->client, conn, server_name, server_info,
2009 silc_id_dup(server_id, SILC_ID_SERVER));
2013 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
2014 silc_free(server_id);
2016 silc_client_command_reply_free(cmd);
2019 static void silc_client_command_reply_users_i_cb(SilcClient client,
2020 SilcClientConnection conn,
2021 SilcChannelEntry *channels,
2022 SilcUInt32 channels_count,
2025 if (!channels_count) {
2026 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2027 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2029 cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
2030 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2031 "%s", silc_get_status_message(cmd->error));
2032 COMMAND_REPLY_ERROR;
2033 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2034 silc_client_command_reply_free(cmd);
2038 silc_client_command_reply_users_i(context, NULL);
2041 SILC_CLIENT_CMD_REPLY_FUNC(users_i)
2043 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2045 COMMAND_CHECK_STATUS_I;
2047 /* Save USERS info */
2048 if (silc_client_command_reply_users_save(
2049 cmd, cmd->status, FALSE,
2050 silc_client_command_reply_users_i_cb,
2051 silc_client_command_reply_users_i))
2055 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
2058 /* Unregister this command reply */
2059 silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS,
2060 NULL, silc_client_command_reply_users_i,
2063 silc_client_command_reply_free(cmd);
2066 /* Private range commands, specific to this implementation (and compatible
2067 with SILC Server >= 0.9). */
2069 SILC_CLIENT_CMD_REPLY_FUNC(connect)
2071 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2072 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2074 if (cmd->error != SILC_STATUS_OK) {
2075 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2076 "%s", silc_get_status_message(cmd->error));
2077 COMMAND_REPLY_ERROR;
2081 /* Notify application */
2082 COMMAND_REPLY((SILC_ARGS));
2085 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT);
2086 silc_client_command_reply_free(cmd);
2089 SILC_CLIENT_CMD_REPLY_FUNC(close)
2091 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2092 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2094 if (cmd->error != SILC_STATUS_OK) {
2095 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2096 "%s", silc_get_status_message(cmd->error));
2097 COMMAND_REPLY_ERROR;
2101 /* Notify application */
2102 COMMAND_REPLY((SILC_ARGS));
2105 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE);
2106 silc_client_command_reply_free(cmd);
2109 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
2111 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
2112 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
2114 if (cmd->error != SILC_STATUS_OK) {
2115 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2116 "%s", silc_get_status_message(cmd->error));
2117 COMMAND_REPLY_ERROR;
2121 /* Notify application */
2122 COMMAND_REPLY((SILC_ARGS));
2125 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN);
2126 silc_client_command_reply_free(cmd);