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(users, USERS),
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 SilcClientCommandReply *cmd;
138 SilcClientCommandReplyContext ctx;
139 SilcCommandPayload payload;
141 unsigned short ident;
143 /* Get command reply payload from packet */
144 payload = silc_command_payload_parse(buffer);
146 /* Silently ignore bad reply packet */
147 SILC_LOG_DEBUG(("Bad command reply packet"));
151 /* Allocate command reply context. This must be free'd by the
152 command reply routine receiving it. */
153 ctx = silc_calloc(1, sizeof(*ctx));
154 ctx->client = client;
156 ctx->payload = payload;
157 ctx->args = silc_command_get_args(ctx->payload);
158 ctx->packet = packet;
159 ident = silc_command_get_ident(ctx->payload);
161 /* Check for pending commands and mark to be exeucted */
162 silc_client_command_pending_check(sock->user_data, ctx,
163 silc_command_get(ctx->payload), ident);
165 /* Execute command reply */
166 command = silc_command_get(ctx->payload);
167 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
168 if (cmd->cmd == command)
171 if (cmd == NULL || !cmd->cb) {
179 /* Returns status message string */
182 silc_client_command_status_message(SilcCommandStatus status)
186 for (i = 0; silc_command_status_messages[i].message; i++) {
187 if (silc_command_status_messages[i].status == status)
191 if (silc_command_status_messages[i].message == NULL)
194 return silc_command_status_messages[i].message;
197 /* Free command reply context and its internals. */
199 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
202 silc_command_free_payload(cmd->payload);
208 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
209 SilcCommandStatus status)
213 unsigned char *id_data;
214 char *nickname = NULL, *username = NULL;
215 char *realname = NULL;
216 SilcClientID *client_id;
217 SilcIDCacheEntry id_cache = NULL;
218 SilcClientEntry client_entry = NULL;
219 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
221 memset(buf, 0, sizeof(buf));
223 argc = silc_argument_get_arg_num(cmd->args);
225 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
231 client_id = silc_id_payload_parse_id(id_data, len);
237 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
239 strncat(buf, nickname, len);
240 strncat(buf, " is ", 4);
243 username = silc_argument_get_arg_type(cmd->args, 4, &len);
245 strncat(buf, username, len);
248 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
250 strncat(buf, " (", 2);
251 strncat(buf, realname, len);
252 strncat(buf, ")", 1);
255 /* Check if we have this client cached already. */
256 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
257 SILC_ID_CLIENT, &id_cache)) {
258 client_entry = silc_calloc(1, sizeof(*client_entry));
259 client_entry->id = client_id;
260 silc_parse_nickname(nickname, &client_entry->nickname,
261 &client_entry->server, &client_entry->num);
262 client_entry->username = strdup(username);
264 client_entry->realname = strdup(realname);
266 /* Add client to cache */
267 silc_idcache_add(conn->client_cache, client_entry->nickname,
268 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
270 client_entry = (SilcClientEntry)id_cache->context;
271 if (client_entry->nickname)
272 silc_free(client_entry->nickname);
273 if (client_entry->server)
274 silc_free(client_entry->server);
275 if (client_entry->username)
276 silc_free(client_entry->username);
277 if (client_entry->realname)
278 silc_free(client_entry->realname);
280 silc_parse_nickname(nickname, &client_entry->nickname,
281 &client_entry->server, &client_entry->num);
282 client_entry->username = strdup(username);
284 client_entry->realname = strdup(realname);
286 id_cache->data = client_entry->nickname;
288 silc_free(client_id);
292 cmd->client->ops->say(cmd->client, conn, "%s", buf);
294 /* Notify application */
295 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
299 /* Received reply for WHOIS command. This maybe called several times
300 for one WHOIS command as server may reply with list of results. */
302 SILC_CLIENT_CMD_REPLY_FUNC(whois)
304 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
305 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
306 SilcCommandStatus status;
309 SILC_LOG_DEBUG(("Start"));
311 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
312 SILC_GET16_MSB(status, tmp);
313 if (status != SILC_STATUS_OK &&
314 status != SILC_STATUS_LIST_START &&
315 status != SILC_STATUS_LIST_ITEM &&
316 status != SILC_STATUS_LIST_END) {
317 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
318 /* Take nickname which may be provided */
319 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
321 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
322 silc_client_command_status_message(status));
324 cmd->client->ops->say(cmd->client, conn, "%s",
325 silc_client_command_status_message(status));
329 cmd->client->ops->say(cmd->client, conn,
330 "%s", silc_client_command_status_message(status));
336 /* Display one whois reply */
337 if (status == SILC_STATUS_OK) {
338 silc_client_command_reply_whois_print(cmd, status);
341 /* XXX list should not be displayed untill all items has been received. */
342 if (status == SILC_STATUS_LIST_START) {
343 silc_client_command_reply_whois_print(cmd, status);
346 if (status == SILC_STATUS_LIST_ITEM) {
347 silc_client_command_reply_whois_print(cmd, status);
350 if (status == SILC_STATUS_LIST_END) {
351 silc_client_command_reply_whois_print(cmd, status);
354 /* Execute any pending command callbacks */
355 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
358 silc_client_command_reply_free(cmd);
361 /* Received reply for WHOWAS command. */
363 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
368 /* Received reply for IDENTIFY command. This maybe called several times
369 for one IDENTIFY command as server may reply with list of results.
370 This is totally silent and does not print anything on screen. */
372 SILC_CLIENT_CMD_REPLY_FUNC(identify)
374 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
375 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
376 SilcClientEntry client_entry;
377 SilcIDCacheEntry id_cache = NULL;
378 SilcCommandStatus status;
381 SILC_LOG_DEBUG(("Start"));
383 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
384 SILC_GET16_MSB(status, tmp);
385 if (status != SILC_STATUS_OK) {
386 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
387 /* Take nickname which may be provided */
388 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
390 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
391 silc_client_command_status_message(status));
393 cmd->client->ops->say(cmd->client, conn, "%s",
394 silc_client_command_status_message(status));
398 cmd->client->ops->say(cmd->client, conn,
399 "%s", silc_client_command_status_message(status));
405 /* Display one whois reply */
406 if (status == SILC_STATUS_OK) {
408 unsigned char *id_data;
411 SilcClientID *client_id;
413 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
416 client_id = silc_id_payload_parse_id(id_data, len);
420 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
421 username = silc_argument_get_arg_type(cmd->args, 4, NULL);
423 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
424 SILC_ID_CLIENT, &id_cache)) {
425 client_entry = silc_calloc(1, sizeof(*client_entry));
426 client_entry->id = client_id;
427 silc_parse_nickname(nickname, &client_entry->nickname,
428 &client_entry->server, &client_entry->num);
430 client_entry->username = strdup(username);
432 /* Add client to cache */
433 silc_idcache_add(conn->client_cache, client_entry->nickname,
434 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
436 client_entry = (SilcClientEntry)id_cache->context;
437 if (client_entry->nickname)
438 silc_free(client_entry->nickname);
439 if (client_entry->server)
440 silc_free(client_entry->server);
441 if (username && client_entry->username)
442 silc_free(client_entry->username);
444 silc_parse_nickname(nickname, &client_entry->nickname,
445 &client_entry->server, &client_entry->num);
448 client_entry->username = strdup(username);
450 id_cache->data = client_entry->nickname;
452 silc_free(client_id);
456 if (status == SILC_STATUS_LIST_START) {
460 if (status == SILC_STATUS_LIST_END) {
464 /* Execute any pending command callbacks */
465 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
468 silc_client_command_reply_free(cmd);
471 /* Received reply for command NICK. If everything went without errors
472 we just received our new Client ID. */
474 SILC_CLIENT_CMD_REPLY_FUNC(nick)
476 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
477 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
478 SilcCommandStatus status;
481 unsigned int argc, len;
483 SILC_LOG_DEBUG(("Start"));
485 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
486 if (status != SILC_STATUS_OK) {
487 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
488 silc_client_command_status_message(status));
493 argc = silc_argument_get_arg_num(cmd->args);
494 if (argc < 2 || argc > 2) {
495 cmd->client->ops->say(cmd->client, conn,
496 "Cannot set nickname: bad reply to command");
501 /* Take received Client ID */
502 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
503 idp = silc_id_payload_parse_data(tmp, len);
508 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
510 /* Notify application */
511 COMMAND_REPLY((ARGS, conn->local_entry));
513 /* Execute any pending command callbacks */
514 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
517 silc_client_command_reply_free(cmd);
520 SILC_CLIENT_CMD_REPLY_FUNC(list)
524 /* Received reply to topic command. */
526 SILC_CLIENT_CMD_REPLY_FUNC(topic)
528 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
529 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
530 SilcCommandStatus status;
531 SilcChannelEntry channel;
532 SilcChannelID *channel_id = NULL;
533 SilcIDCacheEntry id_cache = NULL;
536 unsigned int argc, len;
538 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
539 if (status != SILC_STATUS_OK) {
540 cmd->client->ops->say(cmd->client, conn,
541 "%s", silc_client_command_status_message(status));
543 silc_client_command_reply_free(cmd);
547 argc = silc_argument_get_arg_num(cmd->args);
548 if (argc < 1 || argc > 3) {
553 /* Take Channel ID */
554 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
559 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
563 channel_id = silc_id_payload_parse_id(tmp, len);
567 /* Get the channel name */
568 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
569 SILC_ID_CHANNEL, &id_cache)) {
570 silc_free(channel_id);
575 channel = (SilcChannelEntry)id_cache->context;
577 cmd->client->ops->say(cmd->client, conn,
578 "Topic on channel %s: %s", channel->channel_name,
581 /* Notify application */
582 COMMAND_REPLY((ARGS, channel, topic));
584 /* Execute any pending command callbacks */
585 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
588 silc_client_command_reply_free(cmd);
591 /* Received reply to invite command. */
593 SILC_CLIENT_CMD_REPLY_FUNC(invite)
595 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
596 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
597 SilcCommandStatus status;
600 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
601 SILC_GET16_MSB(status, tmp);
602 if (status != SILC_STATUS_OK) {
603 cmd->client->ops->say(cmd->client, conn,
604 "%s", silc_client_command_status_message(status));
606 silc_client_command_reply_free(cmd);
610 /* Notify application */
611 COMMAND_REPLY((ARGS));
613 /* Execute any pending command callbacks */
614 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
616 silc_client_command_reply_free(cmd);
619 SILC_CLIENT_CMD_REPLY_FUNC(quit)
623 SILC_CLIENT_CMD_REPLY_FUNC(kill)
627 /* Received reply to INFO command. We receive the server ID and some
628 information about the server user requested. */
630 SILC_CLIENT_CMD_REPLY_FUNC(info)
632 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
633 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
634 SilcClient client = cmd->client;
635 SilcCommandStatus status;
638 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
639 SILC_GET16_MSB(status, tmp);
640 if (status != SILC_STATUS_OK) {
641 cmd->client->ops->say(cmd->client, conn,
642 "%s", silc_client_command_status_message(status));
644 silc_client_command_reply_free(cmd);
649 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
653 /* XXX save server id */
655 /* Get server info */
656 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
660 client->ops->say(cmd->client, conn, "Info: %s", tmp);
662 /* Notify application */
663 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
665 /* Execute any pending command callbacks */
666 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
669 silc_client_command_reply_free(cmd);
672 SILC_CLIENT_CMD_REPLY_FUNC(connect)
676 /* Received reply to PING command. The reply time is shown to user. */
678 SILC_CLIENT_CMD_REPLY_FUNC(ping)
680 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
681 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
682 SilcCommandStatus status;
685 time_t diff, curtime;
687 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
688 if (status != SILC_STATUS_OK) {
689 cmd->client->ops->say(cmd->client, conn,
690 "%s", silc_client_command_status_message(status));
695 curtime = time(NULL);
696 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
697 cmd->packet->src_id_type);
703 for (i = 0; i < conn->ping_count; i++) {
704 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
705 diff = curtime - conn->ping[i].start_time;
706 cmd->client->ops->say(cmd->client, conn,
707 "Ping reply from %s: %d second%s",
708 conn->ping[i].dest_name, diff,
709 diff == 1 ? "" : "s");
711 conn->ping[i].start_time = 0;
712 silc_free(conn->ping[i].dest_id);
713 conn->ping[i].dest_id = NULL;
714 silc_free(conn->ping[i].dest_name);
715 conn->ping[i].dest_name = NULL;
717 /* Notify application */
718 COMMAND_REPLY((ARGS));
725 /* Execute any pending command callbacks */
726 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
729 silc_client_command_reply_free(cmd);
732 SILC_CLIENT_CMD_REPLY_FUNC(oper)
736 /* Received reply for JOIN command. */
738 SILC_CLIENT_CMD_REPLY_FUNC(join)
740 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
741 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
742 SilcClient client = cmd->client;
743 SilcCommandStatus status;
744 SilcIDPayload idp = NULL;
745 unsigned int argc, mode, len;
746 char *topic, *tmp, *channel_name = NULL;
749 SILC_LOG_DEBUG(("Start"));
751 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
752 if (status != SILC_STATUS_OK) {
753 cmd->client->ops->say(cmd->client, conn,
754 "%s", silc_client_command_status_message(status));
759 argc = silc_argument_get_arg_num(cmd->args);
760 if (argc < 3 || argc > 9) {
761 cmd->client->ops->say(cmd->client, conn,
762 "Cannot join channel: Bad reply packet");
767 /* Get channel name */
768 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
770 cmd->client->ops->say(cmd->client, conn,
771 "Cannot join channel: Bad reply packet");
775 channel_name = strdup(tmp);
778 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
780 cmd->client->ops->say(cmd->client, conn,
781 "Cannot join channel: Bad reply packet");
783 silc_free(channel_name);
786 idp = silc_id_payload_parse_data(tmp, len);
789 silc_free(channel_name);
793 /* Get channel mode */
794 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
796 SILC_GET32_MSB(mode, tmp);
800 /* Get channel key */
801 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
803 silc_id_payload_free(idp);
804 silc_free(channel_name);
807 keyp = silc_buffer_alloc(len);
808 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
809 silc_buffer_put(keyp, tmp, len);
812 topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
814 /* Save received Channel ID */
815 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
817 silc_id_payload_free(idp);
819 /* Save channel key */
820 silc_client_save_channel_key(conn, keyp, conn->current_channel);
821 silc_buffer_free(keyp);
824 client->ops->say(cmd->client, conn,
825 "Topic for %s: %s", channel_name, topic);
827 /* Notify application */
828 COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
831 /* Execute any pending command callbacks */
832 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
835 silc_client_command_reply_free(cmd);
838 /* Received reply for MOTD command */
840 SILC_CLIENT_CMD_REPLY_FUNC(motd)
842 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
843 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
844 SilcCommandStatus status;
845 unsigned int argc, i;
847 char *motd = NULL, *cp, line[256];
849 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
850 SILC_GET16_MSB(status, tmp);
851 if (status != SILC_STATUS_OK) {
852 cmd->client->ops->say(cmd->client, conn,
853 "%s", silc_client_command_status_message(status));
858 argc = silc_argument_get_arg_num(cmd->args);
865 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
874 if (cp[i++] == '\n') {
875 memset(line, 0, sizeof(line));
876 strncat(line, cp, i - 1);
882 cmd->client->ops->say(cmd->client, conn, "%s", line);
891 /* Notify application */
892 COMMAND_REPLY((ARGS, motd));
894 /* Execute any pending command callbacks */
895 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
898 silc_client_command_reply_free(cmd);
901 SILC_CLIENT_CMD_REPLY_FUNC(umode)
905 /* Received reply for CMODE command. */
907 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
909 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
910 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
911 SilcCommandStatus status;
914 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
915 if (status != SILC_STATUS_OK) {
916 cmd->client->ops->say(cmd->client, conn,
917 "%s", silc_client_command_status_message(status));
922 /* Get channel mode */
923 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
929 /* Notify application */
930 COMMAND_REPLY((ARGS, tmp));
932 /* Execute any pending command callbacks */
933 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
936 silc_client_command_reply_free(cmd);
939 /* Received reply for CUMODE command */
941 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
943 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
944 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
945 SilcCommandStatus status;
946 SilcIDCacheEntry id_cache = NULL;
947 SilcClientID *client_id;
948 unsigned char *tmp, *id;
951 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
952 if (status != SILC_STATUS_OK) {
953 cmd->client->ops->say(cmd->client, conn,
954 "%s", silc_client_command_status_message(status));
959 /* Get channel mode */
960 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
967 id = silc_argument_get_arg_type(cmd->args, 3, &len);
972 client_id = silc_id_payload_parse_id(id, len);
978 /* Get client entry */
979 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
980 SILC_ID_CLIENT, &id_cache)) {
985 /* Notify application */
986 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
987 silc_free(client_id);
989 /* Execute any pending command callbacks */
990 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
993 silc_client_command_reply_free(cmd);
996 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1000 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1004 SILC_CLIENT_CMD_REPLY_FUNC(close)
1008 SILC_CLIENT_CMD_REPLY_FUNC(die)
1012 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1016 /* Reply to LEAVE command. */
1018 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1020 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1021 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1022 SilcCommandStatus status;
1025 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1026 SILC_GET16_MSB(status, tmp);
1027 if (status != SILC_STATUS_OK) {
1028 cmd->client->ops->say(cmd->client, conn,
1029 "%s", silc_client_command_status_message(status));
1030 COMMAND_REPLY_ERROR;
1034 /* Notify application */
1035 COMMAND_REPLY((ARGS));
1037 /* Execute any pending command callbacks */
1038 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
1040 silc_client_command_reply_free(cmd);
1043 /* Reply to USERS command. Received list of client ID's and theirs modes
1044 on the channel we requested. */
1046 SILC_CLIENT_CMD_REPLY_FUNC(users)
1048 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1049 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1050 SilcCommandStatus status;
1051 SilcIDCacheEntry id_cache = NULL;
1052 SilcChannelEntry channel;
1053 SilcChannelUser chu;
1054 SilcChannelID *channel_id = NULL;
1055 SilcBuffer client_id_list;
1056 SilcBuffer client_mode_list;
1058 unsigned int tmp_len, list_count;
1060 unsigned char **res_argv = NULL;
1061 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1063 SILC_LOG_DEBUG(("Start"));
1065 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1066 SILC_GET16_MSB(status, tmp);
1067 if (status != SILC_STATUS_OK) {
1068 cmd->client->ops->say(cmd->client, conn,
1069 "%s", silc_client_command_status_message(status));
1070 COMMAND_REPLY_ERROR;
1074 /* Get channel ID */
1075 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1078 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1082 /* Get the list count */
1083 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1086 SILC_GET32_MSB(list_count, tmp);
1088 /* Get Client ID list */
1089 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1093 client_id_list = silc_buffer_alloc(tmp_len);
1094 silc_buffer_pull_tail(client_id_list, tmp_len);
1095 silc_buffer_put(client_id_list, tmp, tmp_len);
1097 /* Get client mode list */
1098 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1102 client_mode_list = silc_buffer_alloc(tmp_len);
1103 silc_buffer_pull_tail(client_mode_list, tmp_len);
1104 silc_buffer_put(client_mode_list, tmp, tmp_len);
1106 /* Get channel entry */
1107 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1108 SILC_ID_CHANNEL, &id_cache)) {
1109 COMMAND_REPLY_ERROR;
1112 channel = (SilcChannelEntry)id_cache->context;
1114 /* Remove old client list from channel. */
1115 silc_list_start(channel->clients);
1116 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1117 silc_list_del(channel->clients, chu);
1121 /* Cache the received Client ID's and modes. This cache expires
1122 whenever server sends notify message to channel. It means two things;
1123 some user has joined or leaved the channel. XXX! */
1124 for (i = 0; i < list_count; i++) {
1125 unsigned short idp_len;
1127 SilcClientID *client_id;
1128 SilcClientEntry client;
1131 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1133 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1138 SILC_GET32_MSB(mode, client_mode_list->data);
1140 /* Check if we have this client cached already. */
1141 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1142 SILC_ID_CLIENT, &id_cache)) {
1143 /* No we don't have it, query it from the server. Assemble argument
1144 table that will be sent fr the IDENTIFY command later. */
1145 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1147 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1149 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1151 res_argv[res_argc] = client_id_list->data;
1152 res_argv_lens[res_argc] = idp_len;
1153 res_argv_types[res_argc] = res_argc + 3;
1156 /* Found the client, join it to the channel */
1157 client = (SilcClientEntry)id_cache->context;
1158 chu = silc_calloc(1, sizeof(*chu));
1159 chu->client = client;
1161 silc_list_add(channel->clients, chu);
1163 silc_free(client_id);
1167 silc_buffer_pull(client_id_list, idp_len);
1168 silc_buffer_pull(client_mode_list, 4);
1171 /* Query the client information from server if the list included clients
1172 that we don't know about. */
1176 /* Send the IDENTIFY command to server */
1177 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1178 res_argc, res_argv, res_argv_lens,
1179 res_argv_types, ++conn->cmd_ident);
1180 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1181 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1184 /* Register pending command callback. After we've received the IDENTIFY
1185 command reply we will reprocess this command reply by re-calling this
1186 USERS command reply callback. */
1187 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1188 silc_client_command_reply_users, cmd);
1190 silc_buffer_free(res_cmd);
1192 silc_free(channel_id);
1194 silc_free(res_argv);
1195 silc_free(res_argv_lens);
1196 silc_free(res_argv_types);
1200 /* We have all the clients on the channel cached now. Create a nice
1201 output for user interface and notify application. */
1203 if (!cmd->callback) {
1204 /* Server has sent us USERS reply even when we haven't actually sent
1205 USERS command. This is normal behaviour when joining to a channel.
1206 Display some nice information on the user interface. */
1207 int k = 0, len1 = 0, len2 = 0;
1208 char *name_list = NULL;
1210 silc_list_start(channel->clients);
1211 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1212 char *m, *n = chu->client->nickname;
1216 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1218 m = silc_client_chumode_char(chu->mode);
1220 memcpy(name_list + (len1 - len2), m, strlen(m));
1225 memcpy(name_list + (len1 - len2), n, len2);
1226 name_list[len1] = 0;
1228 if (k == silc_list_count(channel->clients) - 1)
1230 memcpy(name_list + len1, " ", 1);
1235 cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1236 channel->channel_name, name_list);
1237 silc_free(name_list);
1240 /* Notify application */
1241 COMMAND_REPLY((ARGS, channel, client_id_list->head,
1242 client_mode_list->head));
1244 /* Execute any pending command callbacks */
1245 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
1247 silc_buffer_free(client_id_list);
1248 silc_buffer_free(client_mode_list);
1252 silc_free(channel_id);
1253 silc_client_command_reply_free(cmd);