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.
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.
36 #include "clientlibincludes.h"
38 /* Client command reply list. */
39 SilcClientCommandReply silc_command_reply_list[] =
41 SILC_CLIENT_CMD_REPLY(whois, WHOIS),
42 SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
43 SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
44 SILC_CLIENT_CMD_REPLY(nick, NICK),
45 SILC_CLIENT_CMD_REPLY(list, LIST),
46 SILC_CLIENT_CMD_REPLY(topic, TOPIC),
47 SILC_CLIENT_CMD_REPLY(invite, INVITE),
48 SILC_CLIENT_CMD_REPLY(quit, QUIT),
49 SILC_CLIENT_CMD_REPLY(kill, KILL),
50 SILC_CLIENT_CMD_REPLY(info, INFO),
51 SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52 SILC_CLIENT_CMD_REPLY(ping, PING),
53 SILC_CLIENT_CMD_REPLY(oper, OPER),
54 SILC_CLIENT_CMD_REPLY(join, JOIN),
55 SILC_CLIENT_CMD_REPLY(motd, MOTD),
56 SILC_CLIENT_CMD_REPLY(umode, UMODE),
57 SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58 SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59 SILC_CLIENT_CMD_REPLY(kick, KICK),
60 SILC_CLIENT_CMD_REPLY(restart, RESTART),
61 SILC_CLIENT_CMD_REPLY(close, CLOSE),
62 SILC_CLIENT_CMD_REPLY(die, DIE),
63 SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64 SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65 SILC_CLIENT_CMD_REPLY(names, NAMES),
70 /* Status message structure. Messages are defined below. */
72 SilcCommandStatus status;
74 } SilcCommandStatusMessage;
76 /* Status messages returned by the server */
77 #define STAT(x) SILC_STATUS_ERR_##x
78 const SilcCommandStatusMessage silc_command_status_messages[] = {
80 { STAT(NO_SUCH_NICK), "No such nickname" },
81 { STAT(NO_SUCH_CHANNEL), "No such channel" },
82 { STAT(NO_SUCH_SERVER), "No such server" },
83 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
84 { STAT(NO_RECIPIENT), "No recipient given" },
85 { STAT(UNKNOWN_COMMAND), "Unknown command" },
86 { STAT(WILDCARDS), "Unknown command" },
87 { STAT(NO_CLIENT_ID), "No Client ID given" },
88 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
89 { STAT(NO_SERVER_ID), "No Server ID given" },
90 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
91 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
92 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
93 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
94 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
95 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
96 { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
97 { STAT(USER_ON_CHANNEL), "User already on the channel" },
98 { STAT(NOT_REGISTERED), "You have not registered" },
99 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
100 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
101 { STAT(PERM_DENIED), "Your host is not among the privileged" },
102 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
103 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
104 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
105 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
106 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
107 { STAT(UNKNOWN_MODE), "Unknown mode" },
108 { STAT(NOT_YOU), "Cannot change mode for other users" },
109 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
110 { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" },
111 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
112 { STAT(BAD_NICKNAME), "Bad nickname" },
113 { STAT(BAD_CHANNEL), "Bad channel name" },
114 { STAT(AUTH_FAILED), "Authentication failed" },
119 /* Command reply operation that is called at the end of all command replys.
120 Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
121 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
122 #define ARGS cmd->client, cmd->sock->user_data, \
123 cmd->payload, TRUE, silc_command_get(cmd->payload), status
125 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
126 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
127 cmd->sock->user_data, cmd->payload, FALSE, \
128 silc_command_get(cmd->payload), status)
130 /* Process received command reply. */
132 void silc_client_command_reply_process(SilcClient client,
133 SilcSocketConnection sock,
134 SilcPacketContext *packet)
136 SilcBuffer buffer = packet->buffer;
137 SilcClientCommandReplyContext ctx;
138 SilcCommandPayload payload;
140 /* Get command reply payload from packet */
141 payload = silc_command_payload_parse(buffer);
143 /* Silently ignore bad reply packet */
144 SILC_LOG_DEBUG(("Bad command reply packet"));
148 /* Allocate command reply context. This must be free'd by the
149 command reply routine receiving it. */
150 ctx = silc_calloc(1, sizeof(*ctx));
151 ctx->client = client;
153 ctx->payload = payload;
154 ctx->args = silc_command_get_args(ctx->payload);
155 ctx->packet = packet;
157 /* Check for pending commands and mark to be exeucted */
158 SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
160 /* Execute command reply */
161 SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
164 /* Returns status message string */
167 silc_client_command_status_message(SilcCommandStatus status)
171 for (i = 0; silc_command_status_messages[i].message; i++) {
172 if (silc_command_status_messages[i].status == status)
176 if (silc_command_status_messages[i].message == NULL)
179 return silc_command_status_messages[i].message;
182 /* Free command reply context and its internals. */
184 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
187 silc_command_free_payload(cmd->payload);
193 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
194 SilcCommandStatus status)
198 unsigned char *id_data;
199 char *nickname = NULL, *username = NULL;
200 char *realname = NULL;
201 SilcClientID *client_id;
202 SilcIDCacheEntry id_cache = NULL;
203 SilcClientEntry client_entry = NULL;
204 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
206 memset(buf, 0, sizeof(buf));
208 argc = silc_argument_get_arg_num(cmd->args);
210 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
216 client_id = silc_id_payload_parse_id(id_data, len);
218 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
220 strncat(buf, nickname, len);
221 strncat(buf, " is ", 4);
224 username = silc_argument_get_arg_type(cmd->args, 4, &len);
226 strncat(buf, username, len);
229 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
231 strncat(buf, " (", 2);
232 strncat(buf, realname, len);
233 strncat(buf, ")", 1);
236 /* Check if we have this client cached already. */
237 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
238 SILC_ID_CLIENT, &id_cache)) {
239 client_entry = silc_calloc(1, sizeof(*client_entry));
240 client_entry->id = client_id;
241 silc_parse_nickname(nickname, &client_entry->nickname,
242 &client_entry->server, &client_entry->num);
243 client_entry->username = strdup(username);
245 /* Add client to cache */
246 silc_idcache_add(conn->client_cache, client_entry->nickname,
247 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
249 client_entry = (SilcClientEntry)id_cache->context;
250 silc_free(client_id);
254 cmd->client->ops->say(cmd->client, conn, "%s", buf);
256 /* Notify application */
257 COMMAND_REPLY((ARGS, client_entry, nickname,
258 username, realname, NULL, NULL));
261 /* Received reply for WHOIS command. This maybe called several times
262 for one WHOIS command as server may reply with list of results. */
264 SILC_CLIENT_CMD_REPLY_FUNC(whois)
266 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
267 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
268 SilcCommandStatus status;
271 SILC_LOG_DEBUG(("Start"));
273 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
274 SILC_GET16_MSB(status, tmp);
275 if (status != SILC_STATUS_OK &&
276 status != SILC_STATUS_LIST_START &&
277 status != SILC_STATUS_LIST_ITEM &&
278 status != SILC_STATUS_LIST_END) {
279 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
280 /* Take nickname which may be provided */
281 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
283 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
284 silc_client_command_status_message(status));
286 cmd->client->ops->say(cmd->client, conn, "%s",
287 silc_client_command_status_message(status));
291 cmd->client->ops->say(cmd->client, conn,
292 "%s", silc_client_command_status_message(status));
298 /* Display one whois reply */
299 if (status == SILC_STATUS_OK) {
300 silc_client_command_reply_whois_print(cmd, status);
303 /* XXX list should not be displayed untill all items has been received. */
304 if (status == SILC_STATUS_LIST_START) {
305 silc_client_command_reply_whois_print(cmd, status);
308 if (status == SILC_STATUS_LIST_ITEM) {
309 silc_client_command_reply_whois_print(cmd, status);
312 if (status == SILC_STATUS_LIST_END) {
313 silc_client_command_reply_whois_print(cmd, status);
316 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
319 silc_client_command_reply_free(cmd);
322 /* Received reply for WHOWAS command. */
324 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
329 /* Received reply for IDENTIFY command. This maybe called several times
330 for one IDENTIFY command as server may reply with list of results.
331 This is totally silent and does not print anything on screen. */
333 SILC_CLIENT_CMD_REPLY_FUNC(identify)
335 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
336 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
337 SilcClientEntry client_entry;
338 SilcCommandStatus status;
341 SILC_LOG_DEBUG(("Start"));
343 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
344 SILC_GET16_MSB(status, tmp);
345 if (status != SILC_STATUS_OK) {
346 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
347 /* Take nickname which may be provided */
348 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
350 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
351 silc_client_command_status_message(status));
353 cmd->client->ops->say(cmd->client, conn, "%s",
354 silc_client_command_status_message(status));
358 cmd->client->ops->say(cmd->client, conn,
359 "%s", silc_client_command_status_message(status));
365 /* Display one whois reply */
366 if (status == SILC_STATUS_OK) {
368 unsigned char *id_data;
372 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
373 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
374 username = silc_argument_get_arg_type(cmd->args, 4, NULL);
376 /* Allocate client entry */
377 client_entry = silc_calloc(1, sizeof(*client_entry));
378 client_entry->id = silc_id_payload_parse_id(id_data, len);
380 silc_parse_nickname(nickname, &client_entry->nickname,
381 &client_entry->server, &client_entry->num);
383 client_entry->username = strdup(username);
385 /* Save received Client ID to ID cache */
386 silc_idcache_add(conn->client_cache, client_entry->nickname,
387 SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
390 if (status == SILC_STATUS_LIST_START) {
394 if (status == SILC_STATUS_LIST_END) {
398 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
401 silc_client_command_reply_free(cmd);
404 /* Received reply for command NICK. If everything went without errors
405 we just received our new Client ID. */
407 SILC_CLIENT_CMD_REPLY_FUNC(nick)
409 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
410 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
411 SilcCommandStatus status;
414 unsigned int argc, len;
416 SILC_LOG_DEBUG(("Start"));
418 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
419 if (status != SILC_STATUS_OK) {
420 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
421 silc_client_command_status_message(status));
426 argc = silc_argument_get_arg_num(cmd->args);
427 if (argc < 2 || argc > 2) {
428 cmd->client->ops->say(cmd->client, conn,
429 "Cannot set nickname: bad reply to command");
434 /* Take received Client ID */
435 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
436 idp = silc_id_payload_parse_data(tmp, len);
437 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
439 /* Notify application */
440 COMMAND_REPLY((ARGS, conn->local_entry));
443 silc_client_command_reply_free(cmd);
446 SILC_CLIENT_CMD_REPLY_FUNC(list)
450 /* Received reply to topic command. */
452 SILC_CLIENT_CMD_REPLY_FUNC(topic)
454 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
455 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
456 SilcCommandStatus status;
457 SilcChannelEntry channel;
458 SilcChannelID *channel_id = NULL;
459 SilcIDCacheEntry id_cache = NULL;
462 unsigned int argc, len;
464 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
465 if (status != SILC_STATUS_OK) {
466 cmd->client->ops->say(cmd->client, conn,
467 "%s", silc_client_command_status_message(status));
469 silc_client_command_reply_free(cmd);
473 argc = silc_argument_get_arg_num(cmd->args);
474 if (argc < 1 || argc > 3) {
479 /* Take Channel ID */
480 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
485 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
489 channel_id = silc_id_payload_parse_id(tmp, len);
491 /* Get the channel name */
492 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
493 SILC_ID_CHANNEL, &id_cache)) {
494 silc_free(channel_id);
499 channel = (SilcChannelEntry)id_cache->context;
501 cmd->client->ops->say(cmd->client, conn,
502 "Topic on channel %s: %s", channel->channel_name,
505 /* Notify application */
506 COMMAND_REPLY((ARGS, channel, topic));
509 silc_client_command_reply_free(cmd);
512 /* Received reply to invite command. */
514 SILC_CLIENT_CMD_REPLY_FUNC(invite)
516 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
517 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
518 SilcCommandStatus status;
521 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
522 SILC_GET16_MSB(status, tmp);
523 if (status != SILC_STATUS_OK) {
524 cmd->client->ops->say(cmd->client, conn,
525 "%s", silc_client_command_status_message(status));
527 silc_client_command_reply_free(cmd);
531 /* Notify application */
532 COMMAND_REPLY((ARGS));
534 silc_client_command_reply_free(cmd);
537 SILC_CLIENT_CMD_REPLY_FUNC(quit)
541 SILC_CLIENT_CMD_REPLY_FUNC(kill)
545 /* Received reply to INFO command. We receive the server ID and some
546 information about the server user requested. */
548 SILC_CLIENT_CMD_REPLY_FUNC(info)
550 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
551 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
552 SilcClient client = cmd->client;
553 SilcCommandStatus status;
556 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
557 SILC_GET16_MSB(status, tmp);
558 if (status != SILC_STATUS_OK) {
559 cmd->client->ops->say(cmd->client, conn,
560 "%s", silc_client_command_status_message(status));
562 silc_client_command_reply_free(cmd);
567 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
571 /* XXX save server id */
573 /* Get server info */
574 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
578 client->ops->say(cmd->client, conn, "Info: %s", tmp);
580 /* Notify application */
581 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
584 silc_client_command_reply_free(cmd);
587 SILC_CLIENT_CMD_REPLY_FUNC(connect)
591 /* Received reply to PING command. The reply time is shown to user. */
593 SILC_CLIENT_CMD_REPLY_FUNC(ping)
595 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
596 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
597 SilcCommandStatus status;
600 time_t diff, curtime;
602 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
603 if (status != SILC_STATUS_OK) {
604 cmd->client->ops->say(cmd->client, conn,
605 "%s", silc_client_command_status_message(status));
610 curtime = time(NULL);
611 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
613 for (i = 0; i < conn->ping_count; i++) {
614 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
615 diff = curtime - conn->ping[i].start_time;
616 cmd->client->ops->say(cmd->client, conn,
617 "Ping reply from %s: %d second%s",
618 conn->ping[i].dest_name, diff,
619 diff == 1 ? "" : "s");
621 conn->ping[i].start_time = 0;
622 silc_free(conn->ping[i].dest_id);
623 conn->ping[i].dest_id = NULL;
624 silc_free(conn->ping[i].dest_name);
625 conn->ping[i].dest_name = NULL;
627 /* Notify application */
628 COMMAND_REPLY((ARGS));
636 silc_client_command_reply_free(cmd);
639 SILC_CLIENT_CMD_REPLY_FUNC(oper)
643 /* Received reply for JOIN command. */
645 SILC_CLIENT_CMD_REPLY_FUNC(join)
647 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
648 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
649 SilcClient client = cmd->client;
650 SilcCommandStatus status;
652 unsigned int argc, mode, len;
653 char *topic, *tmp, *channel_name;
655 SILC_LOG_DEBUG(("Start"));
657 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
658 if (status != SILC_STATUS_OK) {
659 cmd->client->ops->say(cmd->client, conn,
660 "%s", silc_client_command_status_message(status));
665 argc = silc_argument_get_arg_num(cmd->args);
666 if (argc < 3 || argc > 5) {
667 cmd->client->ops->say(cmd->client, conn,
668 "Cannot join channel: Bad reply packet");
673 /* Get channel name */
674 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
676 cmd->client->ops->say(cmd->client, conn,
677 "Cannot join channel: Bad reply packet");
681 channel_name = strdup(tmp);
684 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
686 cmd->client->ops->say(cmd->client, conn,
687 "Cannot join channel: Bad reply packet");
691 idp = silc_id_payload_parse_data(tmp, len);
693 /* Get channel mode */
694 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
696 SILC_GET32_MSB(mode, tmp);
701 topic = silc_argument_get_arg_type(cmd->args, 5, NULL);
703 /* Save received Channel ID */
704 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
706 silc_id_payload_free(idp);
709 client->ops->say(cmd->client, conn,
710 "Topic for %s: %s", channel_name, topic);
712 /* Notify application */
713 COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
717 silc_client_command_reply_free(cmd);
720 /* Received reply for MOTD command */
722 SILC_CLIENT_CMD_REPLY_FUNC(motd)
724 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
725 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
726 SilcCommandStatus status;
727 unsigned int argc, i;
729 char *motd = NULL, *cp, line[256];
731 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
732 SILC_GET16_MSB(status, tmp);
733 if (status != SILC_STATUS_OK) {
734 cmd->client->ops->say(cmd->client, conn,
735 "%s", silc_client_command_status_message(status));
740 argc = silc_argument_get_arg_num(cmd->args);
747 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
756 if (cp[i++] == '\n') {
757 memset(line, 0, sizeof(line));
758 strncat(line, cp, i - 1);
764 cmd->client->ops->say(cmd->client, conn, "%s", line);
773 /* Notify application */
774 COMMAND_REPLY((ARGS, motd));
777 silc_client_command_reply_free(cmd);
780 SILC_CLIENT_CMD_REPLY_FUNC(umode)
784 /* Received reply for CMODE command. */
786 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
788 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
789 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
790 SilcCommandStatus status;
793 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
794 if (status != SILC_STATUS_OK) {
795 cmd->client->ops->say(cmd->client, conn,
796 "%s", silc_client_command_status_message(status));
801 /* Get channel mode */
802 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
808 /* Notify application */
809 COMMAND_REPLY((ARGS, tmp));
812 silc_client_command_reply_free(cmd);
815 /* Received reply for CUMODE command */
817 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
819 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
820 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
821 SilcCommandStatus status;
822 SilcIDCacheEntry id_cache = NULL;
823 SilcClientID *client_id;
824 unsigned char *tmp, *id;
827 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
828 if (status != SILC_STATUS_OK) {
829 cmd->client->ops->say(cmd->client, conn,
830 "%s", silc_client_command_status_message(status));
835 /* Get channel mode */
836 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
843 id = silc_argument_get_arg_type(cmd->args, 3, &len);
848 client_id = silc_id_payload_parse_id(id, len);
850 /* Get client entry */
851 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
852 SILC_ID_CLIENT, &id_cache)) {
857 /* Notify application */
858 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
859 silc_free(client_id);
862 silc_client_command_reply_free(cmd);
865 SILC_CLIENT_CMD_REPLY_FUNC(kick)
869 SILC_CLIENT_CMD_REPLY_FUNC(restart)
873 SILC_CLIENT_CMD_REPLY_FUNC(close)
877 SILC_CLIENT_CMD_REPLY_FUNC(die)
881 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
885 /* Reply to LEAVE command. */
887 SILC_CLIENT_CMD_REPLY_FUNC(leave)
889 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
890 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
891 SilcCommandStatus status;
894 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
895 SILC_GET16_MSB(status, tmp);
896 if (status != SILC_STATUS_OK) {
897 cmd->client->ops->say(cmd->client, conn,
898 "%s", silc_client_command_status_message(status));
903 /* Notify application */
904 COMMAND_REPLY((ARGS));
906 silc_client_command_reply_free(cmd);
909 /* Reply to NAMES command. Received list of client names on the channel
912 SILC_CLIENT_CMD_REPLY_FUNC(names)
914 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
915 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
916 SilcCommandStatus status;
917 SilcIDCacheEntry id_cache = NULL;
918 SilcChannelEntry channel;
919 SilcChannelID *channel_id = NULL;
920 SilcBuffer client_id_list;
921 SilcBuffer client_mode_list;
923 char *name_list, *cp;
924 int i, k, len1, len2, list_count = 0;
926 SILC_LOG_DEBUG(("Start"));
928 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
929 SILC_GET16_MSB(status, tmp);
930 if (status != SILC_STATUS_OK) {
931 cmd->client->ops->say(cmd->client, conn,
932 "%s", silc_client_command_status_message(status));
938 tmp = silc_argument_get_arg_type(cmd->args, 2, &len1);
940 cmd->client->ops->say(cmd->client, conn,
941 "Cannot Channel ID: Bad reply packet");
945 channel_id = silc_id_payload_parse_id(tmp, len1);
947 /* Get the name list of the channel */
948 name_list = silc_argument_get_arg_type(cmd->args, 3, &len1);
950 cmd->client->ops->say(cmd->client, conn,
951 "Cannot get user list: Bad reply packet");
956 /* Get Client ID list */
957 tmp = silc_argument_get_arg_type(cmd->args, 4, &len2);
959 cmd->client->ops->say(cmd->client, conn,
960 "Cannot get user list: Bad reply packet");
965 client_id_list = silc_buffer_alloc(len2);
966 silc_buffer_pull_tail(client_id_list, len2);
967 silc_buffer_put(client_id_list, tmp, len2);
969 /* Get client mode list */
970 tmp = silc_argument_get_arg_type(cmd->args, 5, &len2);
972 cmd->client->ops->say(cmd->client, conn,
973 "Cannot get user list: Bad reply packet");
978 client_mode_list = silc_buffer_alloc(len2);
979 silc_buffer_pull_tail(client_mode_list, len2);
980 silc_buffer_put(client_mode_list, tmp, len2);
982 /* Get the channel name */
983 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
984 SILC_ID_CHANNEL, &id_cache)) {
989 channel = (SilcChannelEntry)id_cache->context;
991 /* If there is pending command we know that user has called this command
992 and we will handle the name list differently. */
994 /* We will resolve all the necessary information about the people
995 on the channel. Only after that will we display the user list. */
996 for (i = 0; i < len1; i++) {
1000 silc_client_command_pending_del(SILC_COMMAND_NAMES);
1002 /* there is no pending callback it means that this command reply
1003 has been received without calling the command, ie. server has sent
1004 the reply without getting the command from us first. This happens
1005 with SILC servers that sends NAMES reply after joining to a channel. */
1007 /* Remove commas from list */
1008 for (i = 0; i < len1; i++)
1009 if (name_list[i] == ',') {
1016 /* Remove old client list from channel, if exists */
1017 if (channel->clients) {
1018 silc_free(channel->clients);
1019 channel->clients = NULL;
1020 channel->clients_count = 0;
1023 /* Allocate room for clients in the channel */
1024 channel->clients = silc_calloc(list_count, sizeof(*channel->clients));
1026 /* Cache the received name list, client ID's and modes. This cache expires
1027 whenever server sends notify message to channel. It means two things;
1028 some user has joined or leaved the channel. */
1030 for (i = 0; i < list_count; i++) {
1031 int nick_len = strcspn(name_list, " ");
1032 unsigned short idp_len;
1034 char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1035 SilcClientID *client_id;
1036 SilcClientEntry client;
1038 memcpy(nickname, name_list, nick_len);
1039 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1041 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1042 silc_buffer_pull(client_id_list, idp_len);
1044 SILC_GET32_MSB(mode, client_mode_list->data);
1045 silc_buffer_pull(client_mode_list, 4);
1047 /* Check if we have this client cached already. */
1048 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1049 SILC_ID_CLIENT, &id_cache)) {
1050 client = silc_calloc(1, sizeof(*client));
1051 client->id = client_id;
1052 silc_parse_nickname(nickname, &client->nickname, &client->server,
1054 silc_free(nickname);
1056 /* Add client to cache */
1057 silc_idcache_add(conn->client_cache, client->nickname, SILC_ID_CLIENT,
1058 client_id, (void *)client, TRUE);
1060 client = (SilcClientEntry)id_cache->context;
1061 silc_free(client_id);
1062 silc_free(nickname);
1066 channel->clients[channel->clients_count].client = client;
1067 channel->clients[channel->clients_count].mode = mode;
1068 channel->clients_count++;
1070 name_list += nick_len + 1;
1074 for (i = 0; i < list_count; i++) {
1076 int nick_len = strcspn(name_list, " ");
1077 char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname));
1078 memcpy(nickname, name_list, nick_len);
1080 for (c = 0, k = 0; k < channel->clients_count; k++) {
1081 if (channel->clients[k].client &&
1082 !strncmp(channel->clients[k].client->nickname,
1083 nickname, strlen(channel->clients[k].client->nickname))) {
1091 memset(t, 0, sizeof(t));
1092 channel->clients[k].client->nickname =
1093 silc_calloc(strlen(nickname) + 8, sizeof(*channel->clients[k].
1095 strncat(channel->clients[k].client->nickname, nickname,
1097 snprintf(t, sizeof(t), " [%d]", c++);
1098 strncat(channel->clients[k].client->nickname, t, strlen(t));
1102 silc_free(nickname);
1107 for (k = 0; k < channel->clients_count; k++) {
1108 char *n = channel->clients[k].client->nickname;
1111 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 1));
1112 memcpy(name_list + (len1 - len2), n, len2);
1113 name_list[len1] = 0;
1115 if (k == channel->clients_count - 1)
1117 memcpy(name_list + len1, " ", 1);
1121 cmd->client->ops->say(cmd->client, conn,
1122 "Users on %s: %s", channel->channel_name, name_list);
1124 /* Notify application */
1125 COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head,
1126 client_mode_list->head));
1128 silc_free(name_list);
1129 silc_buffer_free(client_id_list);
1130 silc_buffer_free(client_mode_list);
1134 silc_free(channel_id);
1135 silc_client_command_reply_free(cmd);