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_parse_payload(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->packet = packet;
144 /* Check for pending commands and mark to be exeucted */
145 SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
147 /* Execute command reply */
148 SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
151 /* Returns status message string */
154 silc_client_command_status_message(SilcCommandStatus status)
158 for (i = 0; silc_command_status_messages[i].message; i++) {
159 if (silc_command_status_messages[i].status == status)
163 if (silc_command_status_messages[i].message == NULL)
166 return silc_command_status_messages[i].message;
169 /* Free command reply context and its internals. */
171 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
174 silc_command_free_payload(cmd->payload);
180 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
181 SilcCommandStatus status)
185 unsigned char *id_data;
186 char *nickname = NULL, *username = NULL;
187 char *realname = NULL;
188 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
190 memset(buf, 0, sizeof(buf));
192 argc = silc_command_get_arg_num(cmd->payload);
193 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
195 nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
197 strncat(buf, nickname, len);
198 strncat(buf, " is ", 4);
201 username = silc_command_get_arg_type(cmd->payload, 4, &len);
203 strncat(buf, username, len);
206 realname = silc_command_get_arg_type(cmd->payload, 5, &len);
208 strncat(buf, " (", 2);
209 strncat(buf, realname, len);
210 strncat(buf, ")", 1);
213 cmd->client->ops->say(cmd->client, conn, "%s", buf);
215 /* Notify application */
216 COMMAND_REPLY((ARGS));
219 /* Received reply for WHOIS command. This maybe called several times
220 for one WHOIS command as server may reply with list of results. */
221 /* Sends to application: (no arguments) */
223 SILC_CLIENT_CMD_REPLY_FUNC(whois)
225 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
226 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
227 SilcCommandStatus status;
230 SILC_LOG_DEBUG(("Start"));
232 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
233 SILC_GET16_MSB(status, tmp);
234 if (status != SILC_STATUS_OK &&
235 status != SILC_STATUS_LIST_START &&
236 status != SILC_STATUS_LIST_ITEM &&
237 status != SILC_STATUS_LIST_END) {
238 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
239 /* Take nickname which may be provided */
240 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
242 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
243 silc_client_command_status_message(status));
245 cmd->client->ops->say(cmd->client, conn, "%s",
246 silc_client_command_status_message(status));
250 cmd->client->ops->say(cmd->client, conn,
251 "%s", silc_client_command_status_message(status));
257 /* Display one whois reply */
258 if (status == SILC_STATUS_OK) {
259 silc_client_command_reply_whois_print(cmd, status);
262 /* XXX list should not be displayed untill all items has been received. */
263 if (status == SILC_STATUS_LIST_START) {
264 silc_client_command_reply_whois_print(cmd, status);
267 if (status == SILC_STATUS_LIST_ITEM) {
268 silc_client_command_reply_whois_print(cmd, status);
271 if (status == SILC_STATUS_LIST_END) {
272 silc_client_command_reply_whois_print(cmd, status);
275 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
278 silc_client_command_reply_free(cmd);
281 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
285 /* Received reply for IDENTIFY command. This maybe called several times
286 for one IDENTIFY command as server may reply with list of results.
287 This is totally silent and does not print anything on screen. */
289 SILC_CLIENT_CMD_REPLY_FUNC(identify)
291 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
292 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
293 SilcClientEntry client_entry;
294 SilcCommandStatus status;
297 SILC_LOG_DEBUG(("Start"));
299 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
300 SILC_GET16_MSB(status, tmp);
301 if (status != SILC_STATUS_OK) {
302 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
303 /* Take nickname which may be provided */
304 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
306 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
307 silc_client_command_status_message(status));
309 cmd->client->ops->say(cmd->client, conn, "%s",
310 silc_client_command_status_message(status));
314 cmd->client->ops->say(cmd->client, conn,
315 "%s", silc_client_command_status_message(status));
321 /* Display one whois reply */
322 if (status == SILC_STATUS_OK) {
323 unsigned char *id_data;
326 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
327 nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
329 /* Allocate client entry */
330 client_entry = silc_calloc(1, sizeof(*client_entry));
331 client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
332 client_entry->nickname = strdup(nickname);
334 /* Save received Client ID to ID cache */
335 silc_idcache_add(conn->client_cache, client_entry->nickname,
336 SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
339 if (status == SILC_STATUS_LIST_START) {
343 if (status == SILC_STATUS_LIST_END) {
347 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
350 silc_client_command_reply_free(cmd);
353 /* Received reply for command NICK. If everything went without errors
354 we just received our new Client ID. */
355 /* Sends to application: char * (nickname). */
357 SILC_CLIENT_CMD_REPLY_FUNC(nick)
359 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
360 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
361 SilcCommandStatus status;
362 unsigned char *tmp, *id_string;
365 SILC_LOG_DEBUG(("Start"));
367 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
368 SILC_GET16_MSB(status, tmp);
369 if (status != SILC_STATUS_OK) {
370 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
371 silc_client_command_status_message(status));
376 argc = silc_command_get_arg_num(cmd->payload);
377 if (argc < 2 || argc > 2) {
378 cmd->client->ops->say(cmd->client, conn,
379 "Cannot set nickname: bad reply to command");
384 /* Take received Client ID */
385 id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
386 silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
388 /* Notify application */
389 COMMAND_REPLY((ARGS, conn->nickname));
392 silc_client_command_reply_free(cmd);
395 SILC_CLIENT_CMD_REPLY_FUNC(list)
399 /* Received reply to topic command. */
400 /* Sends to application: SilcChannelID * (channel_id) and char * (topic) */
402 SILC_CLIENT_CMD_REPLY_FUNC(topic)
404 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
405 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
406 SilcCommandStatus status;
407 SilcChannelEntry channel;
408 SilcChannelID *channel_id = NULL;
409 SilcIDCacheEntry id_cache = NULL;
410 unsigned char *tmp, *id_string;
414 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
415 SILC_GET16_MSB(status, tmp);
416 if (status != SILC_STATUS_OK) {
417 cmd->client->ops->say(cmd->client, conn,
418 "%s", silc_client_command_status_message(status));
419 silc_client_command_reply_free(cmd);
424 argc = silc_command_get_arg_num(cmd->payload);
425 if (argc < 1 || argc > 3) {
430 /* Take Channel ID */
431 id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
435 channel_id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
438 topic = silc_command_get_arg_type(cmd->payload, 3, NULL);
442 /* Get the channel name */
443 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
444 SILC_ID_CHANNEL, &id_cache)) {
449 channel = (SilcChannelEntry)id_cache->context;
451 cmd->client->ops->say(cmd->client, conn,
452 "Topic on channel %s: %s", channel->channel_name,
455 /* Notify application */
456 COMMAND_REPLY((ARGS, channel_id, topic));
460 silc_free(channel_id);
461 silc_client_command_reply_free(cmd);
464 /* Received reply to invite command. */
465 /* Sends to application: (no arguments) */
467 SILC_CLIENT_CMD_REPLY_FUNC(invite)
469 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
470 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
471 SilcCommandStatus status;
474 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
475 SILC_GET16_MSB(status, tmp);
476 if (status != SILC_STATUS_OK) {
477 cmd->client->ops->say(cmd->client, conn,
478 "%s", silc_client_command_status_message(status));
479 silc_client_command_reply_free(cmd);
484 /* Notify application */
485 COMMAND_REPLY((ARGS));
487 silc_client_command_reply_free(cmd);
490 SILC_CLIENT_CMD_REPLY_FUNC(quit)
494 SILC_CLIENT_CMD_REPLY_FUNC(kill)
498 /* Received reply to INFO command. We receive the server ID and some
499 information about the server user requested. */
500 /* Sends to application: char * (server information) */
502 SILC_CLIENT_CMD_REPLY_FUNC(info)
504 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
505 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
506 SilcClient client = cmd->client;
507 SilcCommandStatus status;
510 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
511 SILC_GET16_MSB(status, tmp);
512 if (status != SILC_STATUS_OK) {
513 cmd->client->ops->say(cmd->client, conn,
514 "%s", silc_client_command_status_message(status));
515 silc_client_command_reply_free(cmd);
521 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
525 /* XXX save server id */
527 /* Get server info */
528 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
532 client->ops->say(cmd->client, conn, "Info: %s", tmp);
534 /* Notify application */
535 COMMAND_REPLY((ARGS, (char *)tmp));
538 silc_client_command_reply_free(cmd);
541 SILC_CLIENT_CMD_REPLY_FUNC(connect)
545 /* Received reply to PING command. The reply time is shown to user. */
546 /* Sends to application: (no arguments) */
548 SILC_CLIENT_CMD_REPLY_FUNC(ping)
550 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
551 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
552 SilcCommandStatus status;
556 time_t diff, curtime;
558 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
559 SILC_GET16_MSB(status, tmp);
560 if (status != SILC_STATUS_OK) {
561 cmd->client->ops->say(cmd->client, conn,
562 "%s", silc_client_command_status_message(status));
567 curtime = time(NULL);
568 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
570 for (i = 0; i < conn->ping_count; i++) {
571 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
572 diff = curtime - conn->ping[i].start_time;
573 cmd->client->ops->say(cmd->client, conn,
574 "Ping reply from %s: %d second%s",
575 conn->ping[i].dest_name, diff,
576 diff == 1 ? "" : "s");
578 conn->ping[i].start_time = 0;
579 silc_free(conn->ping[i].dest_id);
580 conn->ping[i].dest_id = NULL;
581 silc_free(conn->ping[i].dest_name);
582 conn->ping[i].dest_name = NULL;
584 /* Notify application */
585 COMMAND_REPLY((ARGS));
591 silc_client_command_reply_free(cmd);
594 SILC_CLIENT_CMD_REPLY_FUNC(oper)
598 /* Received reply for JOIN command. */
600 SILC_CLIENT_CMD_REPLY_FUNC(join)
602 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
603 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
604 SilcClient client = cmd->client;
605 SilcCommandStatus status;
606 unsigned int argc, mode;
607 unsigned char *id_string;
608 char *topic, *tmp, *channel_name;
610 SILC_LOG_DEBUG(("Start"));
612 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
613 SILC_GET16_MSB(status, tmp);
614 if (status != SILC_STATUS_OK) {
615 cmd->client->ops->say(cmd->client, conn,
616 "%s", silc_client_command_status_message(status));
621 argc = silc_command_get_arg_num(cmd->payload);
622 if (argc < 3 || argc > 5) {
623 cmd->client->ops->say(cmd->client, conn,
624 "Cannot join channel: Bad reply packet");
629 /* Get channel name */
630 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
632 cmd->client->ops->say(cmd->client, conn,
633 "Cannot join channel: Bad reply packet");
637 channel_name = strdup(tmp);
640 id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
642 cmd->client->ops->say(cmd->client, conn,
643 "Cannot join channel: Bad reply packet");
648 /* Get channel mode */
649 tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
651 SILC_GET32_MSB(mode, tmp);
656 topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
658 /* Save received Channel ID */
659 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
663 client->ops->say(cmd->client, conn,
664 "Topic for %s: %s", channel_name, topic);
666 /* Notify application */
667 COMMAND_REPLY((ARGS, channel_name, topic));
670 silc_client_command_reply_free(cmd);
673 /* Received reply for MOTD command */
674 /* Sends to application: char * (motd) */
676 SILC_CLIENT_CMD_REPLY_FUNC(motd)
678 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
679 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
680 SilcCommandStatus status;
681 unsigned int argc, i;
683 char *motd = NULL, *cp, line[256];
685 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
686 SILC_GET16_MSB(status, tmp);
687 if (status != SILC_STATUS_OK) {
688 cmd->client->ops->say(cmd->client, conn,
689 "%s", silc_client_command_status_message(status));
694 argc = silc_command_get_arg_num(cmd->payload);
701 motd = silc_command_get_arg_type(cmd->payload, 2, NULL);
710 if (cp[i++] == '\n') {
711 memset(line, 0, sizeof(line));
712 strncat(line, cp, i - 1);
718 cmd->client->ops->say(cmd->client, conn, "%s", line);
727 /* Notify application */
728 COMMAND_REPLY((ARGS, motd));
731 silc_client_command_reply_free(cmd);
734 SILC_CLIENT_CMD_REPLY_FUNC(umode)
738 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
742 SILC_CLIENT_CMD_REPLY_FUNC(kick)
746 SILC_CLIENT_CMD_REPLY_FUNC(restart)
750 SILC_CLIENT_CMD_REPLY_FUNC(close)
754 SILC_CLIENT_CMD_REPLY_FUNC(die)
758 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
762 /* Reply to LEAVE command. */
763 /* Sends to application: (no arguments) */
765 SILC_CLIENT_CMD_REPLY_FUNC(leave)
767 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
768 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
769 SilcCommandStatus status;
772 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
773 SILC_GET16_MSB(status, tmp);
774 if (status != SILC_STATUS_OK) {
775 cmd->client->ops->say(cmd->client, conn,
776 "%s", silc_client_command_status_message(status));
781 /* Notify application */
782 COMMAND_REPLY((ARGS));
784 silc_client_command_reply_free(cmd);
787 /* Reply to NAMES command. Received list of client names on the channel
790 SILC_CLIENT_CMD_REPLY_FUNC(names)
792 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
793 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
794 SilcCommandStatus status;
795 SilcIDCacheEntry id_cache = NULL;
796 SilcChannelEntry channel;
797 SilcChannelID *channel_id = NULL;
798 SilcBuffer client_id_list;
800 char *name_list, *cp;
801 int i, k, len1, len2, list_count = 0;
803 SILC_LOG_DEBUG(("Start"));
805 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
806 SILC_GET16_MSB(status, tmp);
807 if (status != SILC_STATUS_OK) {
808 cmd->client->ops->say(cmd->client, conn,
809 "%s", silc_client_command_status_message(status));
815 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
817 cmd->client->ops->say(cmd->client, conn,
818 "Cannot get user list: Bad reply packet");
822 channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
824 /* Get the name list of the channel */
825 name_list = silc_command_get_arg_type(cmd->payload, 3, &len1);
827 cmd->client->ops->say(cmd->client, conn,
828 "Cannot get user list: Bad reply packet");
833 /* Get Client ID list */
834 tmp = silc_command_get_arg_type(cmd->payload, 4, &len2);
836 cmd->client->ops->say(cmd->client, conn,
837 "Cannot get user list: Bad reply packet");
842 client_id_list = silc_buffer_alloc(len2);
843 silc_buffer_pull_tail(client_id_list, len2);
844 silc_buffer_put(client_id_list, tmp, len2);
846 /* Get the channel name */
847 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
848 SILC_ID_CHANNEL, &id_cache)) {
853 channel = (SilcChannelEntry)id_cache->context;
855 /* If there is pending command we know that user has called this command
856 and we will handle the name list differently. */
858 /* We will resolve all the necessary information about the people
859 on the channel. Only after that will we display the user list. */
860 for (i = 0; i < len1; i++) {
864 silc_client_command_pending_del(SILC_COMMAND_NAMES);
866 /* there is no pending callback it means that this command reply
867 has been received without calling the command, ie. server has sent
868 the reply without getting the command from us first. This happens
869 with SILC servers that sends NAMES reply after joining to a channel. */
871 /* Remove commas from list */
872 for (i = 0; i < len1; i++)
873 if (name_list[i] == ',') {
880 if (channel->clients) {
881 silc_free(channel->clients);
882 channel->clients = NULL;
883 channel->clients_count = 0;
886 /* Cache the received name list and client ID's. This cache expires
887 whenever server sends notify message to channel. It means two things;
888 some user has joined or leaved the channel. */
890 for (i = 0; i < list_count; i++) {
891 int nick_len = strcspn(name_list, " ");
892 char *nickname = silc_calloc(nick_len, sizeof(*nickname));
893 SilcClientID *client_id;
894 SilcClientEntry client;
896 memcpy(nickname, name_list, nick_len);
897 client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT);
898 silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
900 client = silc_calloc(1, sizeof(*client));
901 client->id = client_id;
902 client->nickname = nickname;
904 /* Add client to cache */
905 silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT,
906 client_id, (void *)client, TRUE);
907 name_list = name_list + nick_len + 1;
909 /* Add client to channel */
910 channel->clients = silc_realloc(channel->clients,
911 sizeof(*channel->clients) *
912 (channel->clients_count + 1));
913 channel->clients[channel->clients_count] = client;
914 channel->clients_count++;
918 for (i = 0; i < list_count; i++) {
920 int nick_len = strcspn(name_list, " ");
921 char *nickname = silc_calloc(nick_len, sizeof(*nickname));
922 memcpy(nickname, name_list, nick_len);
924 for (c = 0, k = 0; k < channel->clients_count; k++) {
925 if (channel->clients[k] &&
926 !strncmp(channel->clients[k]->nickname,
927 nickname, strlen(channel->clients[k]->nickname))) {
935 memset(t, 0, sizeof(t));
936 channel->clients[k]->nickname =
937 silc_calloc(strlen(nickname) + 8,
938 sizeof(*channel->clients[k]->nickname));
939 strncat(channel->clients[k]->nickname, nickname, strlen(nickname));
940 snprintf(t, sizeof(t), " [%d]", c++);
941 strncat(channel->clients[k]->nickname, t, strlen(t));
950 for (k = 0; k < channel->clients_count; k++) {
951 char *n = channel->clients[k]->nickname;
954 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 1));
955 memcpy(name_list + (len1 - len2), n, len2);
958 if (k == channel->clients_count - 1)
960 memcpy(name_list + len1, " ", 1);
964 cmd->client->ops->say(cmd->client, conn,
965 "Users on %s: %s", channel->channel_name, name_list);
967 silc_free(name_list);
968 silc_buffer_free(client_id_list);
972 silc_free(channel_id);
973 silc_client_command_reply_free(cmd);