5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 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.
26 #include "clientlibincludes.h"
28 /* Client command reply list. */
29 SilcClientCommandReply silc_command_reply_list[] =
31 SILC_CLIENT_CMD_REPLY(whois, WHOIS),
32 SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
33 SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
34 SILC_CLIENT_CMD_REPLY(nick, NICK),
35 SILC_CLIENT_CMD_REPLY(list, LIST),
36 SILC_CLIENT_CMD_REPLY(topic, TOPIC),
37 SILC_CLIENT_CMD_REPLY(invite, INVITE),
38 SILC_CLIENT_CMD_REPLY(quit, QUIT),
39 SILC_CLIENT_CMD_REPLY(kill, KILL),
40 SILC_CLIENT_CMD_REPLY(info, INFO),
41 SILC_CLIENT_CMD_REPLY(connect, CONNECT),
42 SILC_CLIENT_CMD_REPLY(ping, PING),
43 SILC_CLIENT_CMD_REPLY(oper, OPER),
44 SILC_CLIENT_CMD_REPLY(join, JOIN),
45 SILC_CLIENT_CMD_REPLY(motd, MOTD),
46 SILC_CLIENT_CMD_REPLY(umode, UMODE),
47 SILC_CLIENT_CMD_REPLY(cmode, CMODE),
48 SILC_CLIENT_CMD_REPLY(kick, KICK),
49 SILC_CLIENT_CMD_REPLY(restart, RESTART),
50 SILC_CLIENT_CMD_REPLY(close, CLOSE),
51 SILC_CLIENT_CMD_REPLY(die, DIE),
52 SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
53 SILC_CLIENT_CMD_REPLY(leave, LEAVE),
54 SILC_CLIENT_CMD_REPLY(names, NAMES),
59 /* Status message structure. Messages are defined below. */
61 SilcCommandStatus status;
63 } SilcCommandStatusMessage;
65 /* Status messages returned by the server */
66 #define STAT(x) SILC_STATUS_ERR_##x
67 const SilcCommandStatusMessage silc_command_status_messages[] = {
69 { STAT(NO_SUCH_NICK), "No such nickname" },
70 { STAT(NO_SUCH_CHANNEL), "No such channel" },
71 { STAT(NO_SUCH_SERVER), "No such server" },
72 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
73 { STAT(NO_RECIPIENT), "No recipient given" },
74 { STAT(UNKNOWN_COMMAND), "Unknown command" },
75 { STAT(WILDCARDS), "Unknown command" },
76 { STAT(NO_CLIENT_ID), "No Client ID given" },
77 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
78 { STAT(NO_SERVER_ID), "No Server ID given" },
79 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
80 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
81 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
82 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
83 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
84 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
85 { STAT(USER_ON_CHANNEL), "User already on channel" },
86 { STAT(NOT_REGISTERED), "You have not registered" },
87 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
88 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
89 { STAT(PERM_DENIED), "Your host is not among the privileged" },
90 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
91 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
92 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
93 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
94 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
95 { STAT(UNKNOWN_MODE), "Unknown mode" },
96 { STAT(NOT_YOU), "Cannot change mode for other users" },
97 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
98 { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" },
99 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
100 { STAT(BAD_NICKNAME), "Bad nickname" },
101 { STAT(BAD_CHANNEL), "Bad channel name" },
102 { STAT(AUTH_FAILED), "Authentication failed" },
107 /* Command reply operation that is called at the end of all command replys.
108 Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
109 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
110 #define ARGS cmd->client, cmd->sock->user_data, \
111 cmd->payload, TRUE, status, silc_command_get(cmd->payload)
113 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
114 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
115 cmd->sock->user_data, cmd->payload, FALSE, status, \
116 silc_command_get(cmd->payload))
118 /* Process received command reply. */
120 void silc_client_command_reply_process(SilcClient client,
121 SilcSocketConnection sock,
122 SilcPacketContext *packet)
124 SilcBuffer buffer = packet->buffer;
125 SilcClientCommandReplyContext ctx;
126 SilcCommandPayload payload;
128 /* Get command reply payload from packet */
129 payload = silc_command_payload_parse(buffer);
131 /* Silently ignore bad reply packet */
132 SILC_LOG_DEBUG(("Bad command reply packet"));
136 /* Allocate command reply context. This must be free'd by the
137 command reply routine receiving it. */
138 ctx = silc_calloc(1, sizeof(*ctx));
139 ctx->client = client;
141 ctx->payload = payload;
142 ctx->args = silc_command_get_args(ctx->payload);
143 ctx->packet = packet;
145 /* Check for pending commands and mark to be exeucted */
146 SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
148 /* Execute command reply */
149 SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
152 /* Returns status message string */
155 silc_client_command_status_message(SilcCommandStatus status)
159 for (i = 0; silc_command_status_messages[i].message; i++) {
160 if (silc_command_status_messages[i].status == status)
164 if (silc_command_status_messages[i].message == NULL)
167 return silc_command_status_messages[i].message;
170 /* Free command reply context and its internals. */
172 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
175 silc_command_free_payload(cmd->payload);
181 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
182 SilcCommandStatus status)
186 unsigned char *id_data;
187 char *nickname = NULL, *username = NULL;
188 char *realname = NULL;
189 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
191 memset(buf, 0, sizeof(buf));
193 argc = silc_argument_get_arg_num(cmd->args);
194 id_data = silc_argument_get_arg_type(cmd->args, 2, NULL);
196 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
198 strncat(buf, nickname, len);
199 strncat(buf, " is ", 4);
202 username = silc_argument_get_arg_type(cmd->args, 4, &len);
204 strncat(buf, username, len);
207 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
209 strncat(buf, " (", 2);
210 strncat(buf, realname, len);
211 strncat(buf, ")", 1);
214 cmd->client->ops->say(cmd->client, conn, "%s", buf);
216 /* Notify application */
217 COMMAND_REPLY((ARGS));
220 /* Received reply for WHOIS command. This maybe called several times
221 for one WHOIS command as server may reply with list of results. */
222 /* Sends to application: (no arguments) */
224 SILC_CLIENT_CMD_REPLY_FUNC(whois)
226 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
227 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
228 SilcCommandStatus status;
231 SILC_LOG_DEBUG(("Start"));
233 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
234 SILC_GET16_MSB(status, tmp);
235 if (status != SILC_STATUS_OK &&
236 status != SILC_STATUS_LIST_START &&
237 status != SILC_STATUS_LIST_ITEM &&
238 status != SILC_STATUS_LIST_END) {
239 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
240 /* Take nickname which may be provided */
241 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
243 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
244 silc_client_command_status_message(status));
246 cmd->client->ops->say(cmd->client, conn, "%s",
247 silc_client_command_status_message(status));
251 cmd->client->ops->say(cmd->client, conn,
252 "%s", silc_client_command_status_message(status));
258 /* Display one whois reply */
259 if (status == SILC_STATUS_OK) {
260 silc_client_command_reply_whois_print(cmd, status);
263 /* XXX list should not be displayed untill all items has been received. */
264 if (status == SILC_STATUS_LIST_START) {
265 silc_client_command_reply_whois_print(cmd, status);
268 if (status == SILC_STATUS_LIST_ITEM) {
269 silc_client_command_reply_whois_print(cmd, status);
272 if (status == SILC_STATUS_LIST_END) {
273 silc_client_command_reply_whois_print(cmd, status);
276 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
279 silc_client_command_reply_free(cmd);
282 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
286 /* Received reply for IDENTIFY command. This maybe called several times
287 for one IDENTIFY command as server may reply with list of results.
288 This is totally silent and does not print anything on screen. */
290 SILC_CLIENT_CMD_REPLY_FUNC(identify)
292 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
293 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
294 SilcClientEntry client_entry;
295 SilcCommandStatus status;
298 SILC_LOG_DEBUG(("Start"));
300 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
301 SILC_GET16_MSB(status, tmp);
302 if (status != SILC_STATUS_OK) {
303 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
304 /* Take nickname which may be provided */
305 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
307 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
308 silc_client_command_status_message(status));
310 cmd->client->ops->say(cmd->client, conn, "%s",
311 silc_client_command_status_message(status));
315 cmd->client->ops->say(cmd->client, conn,
316 "%s", silc_client_command_status_message(status));
322 /* Display one whois reply */
323 if (status == SILC_STATUS_OK) {
324 unsigned char *id_data;
327 id_data = silc_argument_get_arg_type(cmd->args, 2, NULL);
328 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
330 /* Allocate client entry */
331 client_entry = silc_calloc(1, sizeof(*client_entry));
332 client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
333 client_entry->nickname = strdup(nickname);
335 /* Save received Client ID to ID cache */
336 silc_idcache_add(conn->client_cache, client_entry->nickname,
337 SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
340 if (status == SILC_STATUS_LIST_START) {
344 if (status == SILC_STATUS_LIST_END) {
348 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
351 silc_client_command_reply_free(cmd);
354 /* Received reply for command NICK. If everything went without errors
355 we just received our new Client ID. */
356 /* Sends to application: char * (nickname). */
358 SILC_CLIENT_CMD_REPLY_FUNC(nick)
360 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
361 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
362 SilcCommandStatus status;
363 unsigned char *tmp, *id_string;
366 SILC_LOG_DEBUG(("Start"));
368 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
369 SILC_GET16_MSB(status, tmp);
370 if (status != SILC_STATUS_OK) {
371 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
372 silc_client_command_status_message(status));
377 argc = silc_argument_get_arg_num(cmd->args);
378 if (argc < 2 || argc > 2) {
379 cmd->client->ops->say(cmd->client, conn,
380 "Cannot set nickname: bad reply to command");
385 /* Take received Client ID */
386 id_string = silc_argument_get_arg_type(cmd->args, 2, NULL);
387 silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
389 /* Notify application */
390 COMMAND_REPLY((ARGS, conn->nickname));
393 silc_client_command_reply_free(cmd);
396 SILC_CLIENT_CMD_REPLY_FUNC(list)
400 /* Received reply to topic command. */
401 /* Sends to application: SilcChannelID * (channel_id) and char * (topic) */
403 SILC_CLIENT_CMD_REPLY_FUNC(topic)
405 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
406 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
407 SilcCommandStatus status;
408 SilcChannelEntry channel;
409 SilcChannelID *channel_id = NULL;
410 SilcIDCacheEntry id_cache = NULL;
411 unsigned char *tmp, *id_string;
415 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
416 SILC_GET16_MSB(status, tmp);
417 if (status != SILC_STATUS_OK) {
418 cmd->client->ops->say(cmd->client, conn,
419 "%s", silc_client_command_status_message(status));
420 silc_client_command_reply_free(cmd);
425 argc = silc_argument_get_arg_num(cmd->args);
426 if (argc < 1 || argc > 3) {
431 /* Take Channel ID */
432 id_string = silc_argument_get_arg_type(cmd->args, 2, NULL);
436 channel_id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
439 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
443 /* Get the channel name */
444 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
445 SILC_ID_CHANNEL, &id_cache)) {
450 channel = (SilcChannelEntry)id_cache->context;
452 cmd->client->ops->say(cmd->client, conn,
453 "Topic on channel %s: %s", channel->channel_name,
456 /* Notify application */
457 COMMAND_REPLY((ARGS, channel_id, topic));
461 silc_free(channel_id);
462 silc_client_command_reply_free(cmd);
465 /* Received reply to invite command. */
466 /* Sends to application: (no arguments) */
468 SILC_CLIENT_CMD_REPLY_FUNC(invite)
470 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
471 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
472 SilcCommandStatus status;
475 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
476 SILC_GET16_MSB(status, tmp);
477 if (status != SILC_STATUS_OK) {
478 cmd->client->ops->say(cmd->client, conn,
479 "%s", silc_client_command_status_message(status));
480 silc_client_command_reply_free(cmd);
485 /* Notify application */
486 COMMAND_REPLY((ARGS));
488 silc_client_command_reply_free(cmd);
491 SILC_CLIENT_CMD_REPLY_FUNC(quit)
495 SILC_CLIENT_CMD_REPLY_FUNC(kill)
499 /* Received reply to INFO command. We receive the server ID and some
500 information about the server user requested. */
501 /* Sends to application: char * (server information) */
503 SILC_CLIENT_CMD_REPLY_FUNC(info)
505 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
506 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
507 SilcClient client = cmd->client;
508 SilcCommandStatus status;
511 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
512 SILC_GET16_MSB(status, tmp);
513 if (status != SILC_STATUS_OK) {
514 cmd->client->ops->say(cmd->client, conn,
515 "%s", silc_client_command_status_message(status));
516 silc_client_command_reply_free(cmd);
522 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
526 /* XXX save server id */
528 /* Get server info */
529 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
533 client->ops->say(cmd->client, conn, "Info: %s", tmp);
535 /* Notify application */
536 COMMAND_REPLY((ARGS, (char *)tmp));
539 silc_client_command_reply_free(cmd);
542 SILC_CLIENT_CMD_REPLY_FUNC(connect)
546 /* Received reply to PING command. The reply time is shown to user. */
547 /* Sends to application: (no arguments) */
549 SILC_CLIENT_CMD_REPLY_FUNC(ping)
551 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
552 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
553 SilcCommandStatus status;
557 time_t diff, curtime;
559 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
560 SILC_GET16_MSB(status, tmp);
561 if (status != SILC_STATUS_OK) {
562 cmd->client->ops->say(cmd->client, conn,
563 "%s", silc_client_command_status_message(status));
568 curtime = time(NULL);
569 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
571 for (i = 0; i < conn->ping_count; i++) {
572 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
573 diff = curtime - conn->ping[i].start_time;
574 cmd->client->ops->say(cmd->client, conn,
575 "Ping reply from %s: %d second%s",
576 conn->ping[i].dest_name, diff,
577 diff == 1 ? "" : "s");
579 conn->ping[i].start_time = 0;
580 silc_free(conn->ping[i].dest_id);
581 conn->ping[i].dest_id = NULL;
582 silc_free(conn->ping[i].dest_name);
583 conn->ping[i].dest_name = NULL;
585 /* Notify application */
586 COMMAND_REPLY((ARGS));
592 silc_client_command_reply_free(cmd);
595 SILC_CLIENT_CMD_REPLY_FUNC(oper)
599 /* Received reply for JOIN command. */
601 SILC_CLIENT_CMD_REPLY_FUNC(join)
603 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
604 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
605 SilcClient client = cmd->client;
606 SilcCommandStatus status;
607 unsigned int argc, mode;
608 unsigned char *id_string;
609 char *topic, *tmp, *channel_name;
611 SILC_LOG_DEBUG(("Start"));
613 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
614 SILC_GET16_MSB(status, tmp);
615 if (status != SILC_STATUS_OK) {
616 cmd->client->ops->say(cmd->client, conn,
617 "%s", silc_client_command_status_message(status));
622 argc = silc_argument_get_arg_num(cmd->args);
623 if (argc < 3 || argc > 5) {
624 cmd->client->ops->say(cmd->client, conn,
625 "Cannot join channel: Bad reply packet");
630 /* Get channel name */
631 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
633 cmd->client->ops->say(cmd->client, conn,
634 "Cannot join channel: Bad reply packet");
638 channel_name = strdup(tmp);
641 id_string = silc_argument_get_arg_type(cmd->args, 3, NULL);
643 cmd->client->ops->say(cmd->client, conn,
644 "Cannot join channel: Bad reply packet");
649 /* Get channel mode */
650 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
652 SILC_GET32_MSB(mode, tmp);
657 topic = silc_argument_get_arg_type(cmd->args, 5, NULL);
659 /* Save received Channel ID */
660 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
664 client->ops->say(cmd->client, conn,
665 "Topic for %s: %s", channel_name, topic);
667 /* Notify application */
668 COMMAND_REPLY((ARGS, channel_name, topic));
671 silc_client_command_reply_free(cmd);
674 /* Received reply for MOTD command */
675 /* Sends to application: char * (motd) */
677 SILC_CLIENT_CMD_REPLY_FUNC(motd)
679 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
680 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
681 SilcCommandStatus status;
682 unsigned int argc, i;
684 char *motd = NULL, *cp, line[256];
686 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
687 SILC_GET16_MSB(status, tmp);
688 if (status != SILC_STATUS_OK) {
689 cmd->client->ops->say(cmd->client, conn,
690 "%s", silc_client_command_status_message(status));
695 argc = silc_argument_get_arg_num(cmd->args);
702 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
711 if (cp[i++] == '\n') {
712 memset(line, 0, sizeof(line));
713 strncat(line, cp, i - 1);
719 cmd->client->ops->say(cmd->client, conn, "%s", line);
728 /* Notify application */
729 COMMAND_REPLY((ARGS, motd));
732 silc_client_command_reply_free(cmd);
735 SILC_CLIENT_CMD_REPLY_FUNC(umode)
739 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
743 SILC_CLIENT_CMD_REPLY_FUNC(kick)
747 SILC_CLIENT_CMD_REPLY_FUNC(restart)
751 SILC_CLIENT_CMD_REPLY_FUNC(close)
755 SILC_CLIENT_CMD_REPLY_FUNC(die)
759 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
763 /* Reply to LEAVE command. */
764 /* Sends to application: (no arguments) */
766 SILC_CLIENT_CMD_REPLY_FUNC(leave)
768 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
769 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
770 SilcCommandStatus status;
773 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
774 SILC_GET16_MSB(status, tmp);
775 if (status != SILC_STATUS_OK) {
776 cmd->client->ops->say(cmd->client, conn,
777 "%s", silc_client_command_status_message(status));
782 /* Notify application */
783 COMMAND_REPLY((ARGS));
785 silc_client_command_reply_free(cmd);
788 /* Reply to NAMES command. Received list of client names on the channel
791 SILC_CLIENT_CMD_REPLY_FUNC(names)
793 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
794 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
795 SilcCommandStatus status;
796 SilcIDCacheEntry id_cache = NULL;
797 SilcChannelEntry channel;
798 SilcChannelID *channel_id = NULL;
799 SilcBuffer client_id_list;
801 char *name_list, *cp;
802 int i, k, len1, len2, list_count = 0;
804 SILC_LOG_DEBUG(("Start"));
806 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
807 SILC_GET16_MSB(status, tmp);
808 if (status != SILC_STATUS_OK) {
809 cmd->client->ops->say(cmd->client, conn,
810 "%s", silc_client_command_status_message(status));
816 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
818 cmd->client->ops->say(cmd->client, conn,
819 "Cannot get user list: Bad reply packet");
823 channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
825 /* Get the name list of the channel */
826 name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
828 cmd->client->ops->say(cmd->client, conn,
829 "Cannot get user list: Bad reply packet");
834 /* Get Client ID list */
835 tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
837 cmd->client->ops->say(cmd->client, conn,
838 "Cannot get user list: Bad reply packet");
843 client_id_list = silc_buffer_alloc(len2);
844 silc_buffer_pull_tail(client_id_list, len2);
845 silc_buffer_put(client_id_list, tmp, len2);
847 /* Get the channel name */
848 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
849 SILC_ID_CHANNEL, &id_cache)) {
854 channel = (SilcChannelEntry)id_cache->context;
856 /* If there is pending command we know that user has called this command
857 and we will handle the name list differently. */
859 /* We will resolve all the necessary information about the people
860 on the channel. Only after that will we display the user list. */
861 for (i = 0; i < len1; i++) {
865 silc_client_command_pending_del(SILC_COMMAND_NAMES);
867 /* there is no pending callback it means that this command reply
868 has been received without calling the command, ie. server has sent
869 the reply without getting the command from us first. This happens
870 with SILC servers that sends NAMES reply after joining to a channel. */
872 /* Remove commas from list */
873 for (i = 0; i < len1; i++)
874 if (name_list[i] == ',') {
881 if (channel->clients) {
882 silc_free(channel->clients);
883 channel->clients = NULL;
884 channel->clients_count = 0;
887 /* Cache the received name list and client ID's. This cache expires
888 whenever server sends notify message to channel. It means two things;
889 some user has joined or leaved the channel. */
891 for (i = 0; i < list_count; i++) {
892 int nick_len = strcspn(name_list, " ");
893 char *nickname = silc_calloc(nick_len, sizeof(*nickname));
894 SilcClientID *client_id;
895 SilcClientEntry client;
897 memcpy(nickname, name_list, nick_len);
898 client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT);
899 silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
901 client = silc_calloc(1, sizeof(*client));
902 client->id = client_id;
903 client->nickname = nickname;
905 /* Add client to cache */
906 silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT,
907 client_id, (void *)client, TRUE);
908 name_list = name_list + nick_len + 1;
910 /* Add client to channel */
911 channel->clients = silc_realloc(channel->clients,
912 sizeof(*channel->clients) *
913 (channel->clients_count + 1));
914 channel->clients[channel->clients_count] = client;
915 channel->clients_count++;
919 for (i = 0; i < list_count; i++) {
921 int nick_len = strcspn(name_list, " ");
922 char *nickname = silc_calloc(nick_len, sizeof(*nickname));
923 memcpy(nickname, name_list, nick_len);
925 for (c = 0, k = 0; k < channel->clients_count; k++) {
926 if (channel->clients[k] &&
927 !strncmp(channel->clients[k]->nickname,
928 nickname, strlen(channel->clients[k]->nickname))) {
936 memset(t, 0, sizeof(t));
937 channel->clients[k]->nickname =
938 silc_calloc(strlen(nickname) + 8,
939 sizeof(*channel->clients[k]->nickname));
940 strncat(channel->clients[k]->nickname, nickname, strlen(nickname));
941 snprintf(t, sizeof(t), " [%d]", c++);
942 strncat(channel->clients[k]->nickname, t, strlen(t));
951 for (k = 0; k < channel->clients_count; k++) {
952 char *n = channel->clients[k]->nickname;
955 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 1));
956 memcpy(name_list + (len1 - len2), n, len2);
959 if (k == channel->clients_count - 1)
961 memcpy(name_list + len1, " ", 1);
965 cmd->client->ops->say(cmd->client, conn,
966 "Users on %s: %s", channel->channel_name, name_list);
968 silc_free(name_list);
969 silc_buffer_free(client_id_list);
973 silc_free(channel_id);
974 silc_client_command_reply_free(cmd);