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(kill, KILL),
49 SILC_CLIENT_CMD_REPLY(info, INFO),
50 SILC_CLIENT_CMD_REPLY(connect, CONNECT),
51 SILC_CLIENT_CMD_REPLY(ping, PING),
52 SILC_CLIENT_CMD_REPLY(oper, OPER),
53 SILC_CLIENT_CMD_REPLY(join, JOIN),
54 SILC_CLIENT_CMD_REPLY(motd, MOTD),
55 SILC_CLIENT_CMD_REPLY(umode, UMODE),
56 SILC_CLIENT_CMD_REPLY(cmode, CMODE),
57 SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
58 SILC_CLIENT_CMD_REPLY(kick, KICK),
59 SILC_CLIENT_CMD_REPLY(restart, RESTART),
60 SILC_CLIENT_CMD_REPLY(close, CLOSE),
61 SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
62 SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
63 SILC_CLIENT_CMD_REPLY(leave, LEAVE),
64 SILC_CLIENT_CMD_REPLY(users, USERS),
69 /* Status message structure. Messages are defined below. */
71 SilcCommandStatus status;
73 } SilcCommandStatusMessage;
75 /* Status messages returned by the server */
76 #define STAT(x) SILC_STATUS_ERR_##x
77 const SilcCommandStatusMessage silc_command_status_messages[] = {
79 { STAT(NO_SUCH_NICK), "No such nickname" },
80 { STAT(NO_SUCH_CHANNEL), "No such channel" },
81 { STAT(NO_SUCH_SERVER), "No such server" },
82 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
83 { STAT(NO_RECIPIENT), "No recipient given" },
84 { STAT(UNKNOWN_COMMAND), "Unknown command" },
85 { STAT(WILDCARDS), "Unknown command" },
86 { STAT(NO_CLIENT_ID), "No Client ID given" },
87 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
88 { STAT(NO_SERVER_ID), "No Server ID given" },
89 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
90 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
91 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
92 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
93 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
94 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
95 { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
96 { STAT(USER_ON_CHANNEL), "User already on the channel" },
97 { STAT(NOT_REGISTERED), "You have not registered" },
98 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
99 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
100 { STAT(PERM_DENIED), "Your host is not among the privileged" },
101 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
102 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
103 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
104 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
105 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
106 { STAT(UNKNOWN_MODE), "Unknown mode" },
107 { STAT(NOT_YOU), "Cannot change mode for other users" },
108 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
109 { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
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;
287 silc_idcache_sort_by_data(conn->client_cache);
289 silc_free(client_id);
293 cmd->client->ops->say(cmd->client, conn, "%s", buf);
295 /* Notify application */
296 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
300 /* Received reply for WHOIS command. This maybe called several times
301 for one WHOIS command as server may reply with list of results. */
303 SILC_CLIENT_CMD_REPLY_FUNC(whois)
305 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
306 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
307 SilcCommandStatus status;
310 SILC_LOG_DEBUG(("Start"));
312 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
313 SILC_GET16_MSB(status, tmp);
314 if (status != SILC_STATUS_OK &&
315 status != SILC_STATUS_LIST_START &&
316 status != SILC_STATUS_LIST_ITEM &&
317 status != SILC_STATUS_LIST_END) {
318 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
319 /* Take nickname which may be provided */
320 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
322 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
323 silc_client_command_status_message(status));
325 cmd->client->ops->say(cmd->client, conn, "%s",
326 silc_client_command_status_message(status));
330 cmd->client->ops->say(cmd->client, conn,
331 "%s", silc_client_command_status_message(status));
337 /* Display one whois reply */
338 if (status == SILC_STATUS_OK) {
339 silc_client_command_reply_whois_print(cmd, status);
342 /* XXX list should not be displayed untill all items has been received. */
343 if (status == SILC_STATUS_LIST_START) {
344 silc_client_command_reply_whois_print(cmd, status);
347 if (status == SILC_STATUS_LIST_ITEM) {
348 silc_client_command_reply_whois_print(cmd, status);
351 if (status == SILC_STATUS_LIST_END) {
352 silc_client_command_reply_whois_print(cmd, status);
355 /* Execute any pending command callbacks */
356 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
359 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
360 silc_client_command_reply_free(cmd);
363 /* Received reply for WHOWAS command. */
365 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
370 /* Received reply for IDENTIFY command. This maybe called several times
371 for one IDENTIFY command as server may reply with list of results.
372 This is totally silent and does not print anything on screen. */
374 SILC_CLIENT_CMD_REPLY_FUNC(identify)
376 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
377 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
378 SilcClientEntry client_entry;
379 SilcIDCacheEntry id_cache = NULL;
380 SilcCommandStatus status;
383 SILC_LOG_DEBUG(("Start"));
385 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
386 SILC_GET16_MSB(status, tmp);
387 if (status != SILC_STATUS_OK) {
388 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
389 /* Take nickname which may be provided */
390 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
392 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
393 silc_client_command_status_message(status));
395 cmd->client->ops->say(cmd->client, conn, "%s",
396 silc_client_command_status_message(status));
400 cmd->client->ops->say(cmd->client, conn,
401 "%s", silc_client_command_status_message(status));
407 /* Display one whois reply */
408 if (status == SILC_STATUS_OK) {
410 unsigned char *id_data;
413 SilcClientID *client_id;
415 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
418 client_id = silc_id_payload_parse_id(id_data, len);
422 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
423 username = silc_argument_get_arg_type(cmd->args, 4, NULL);
425 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
426 SILC_ID_CLIENT, &id_cache)) {
427 client_entry = silc_calloc(1, sizeof(*client_entry));
428 client_entry->id = client_id;
429 silc_parse_nickname(nickname, &client_entry->nickname,
430 &client_entry->server, &client_entry->num);
432 client_entry->username = strdup(username);
434 /* Add client to cache */
435 silc_idcache_add(conn->client_cache, client_entry->nickname,
436 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
438 client_entry = (SilcClientEntry)id_cache->context;
439 if (client_entry->nickname)
440 silc_free(client_entry->nickname);
441 if (client_entry->server)
442 silc_free(client_entry->server);
443 if (username && client_entry->username)
444 silc_free(client_entry->username);
446 silc_parse_nickname(nickname, &client_entry->nickname,
447 &client_entry->server, &client_entry->num);
450 client_entry->username = strdup(username);
452 id_cache->data = client_entry->nickname;
453 silc_idcache_sort_by_data(conn->client_cache);
455 silc_free(client_id);
459 if (status == SILC_STATUS_LIST_START) {
463 if (status == SILC_STATUS_LIST_END) {
467 /* Execute any pending command callbacks */
468 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
471 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
472 silc_client_command_reply_free(cmd);
475 /* Received reply for command NICK. If everything went without errors
476 we just received our new Client ID. */
478 SILC_CLIENT_CMD_REPLY_FUNC(nick)
480 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
481 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
482 SilcCommandStatus status;
485 unsigned int argc, len;
487 SILC_LOG_DEBUG(("Start"));
489 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
490 if (status != SILC_STATUS_OK) {
491 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
492 silc_client_command_status_message(status));
497 argc = silc_argument_get_arg_num(cmd->args);
498 if (argc < 2 || argc > 2) {
499 cmd->client->ops->say(cmd->client, conn,
500 "Cannot set nickname: bad reply to command");
505 /* Take received Client ID */
506 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
507 idp = silc_id_payload_parse_data(tmp, len);
512 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
514 /* Notify application */
515 COMMAND_REPLY((ARGS, conn->local_entry));
517 /* Execute any pending command callbacks */
518 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
521 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
522 silc_client_command_reply_free(cmd);
525 SILC_CLIENT_CMD_REPLY_FUNC(list)
529 /* Received reply to topic command. */
531 SILC_CLIENT_CMD_REPLY_FUNC(topic)
533 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
534 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
535 SilcCommandStatus status;
536 SilcChannelEntry channel;
537 SilcChannelID *channel_id = NULL;
538 SilcIDCacheEntry id_cache = NULL;
541 unsigned int argc, len;
543 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
544 if (status != SILC_STATUS_OK) {
545 cmd->client->ops->say(cmd->client, conn,
546 "%s", silc_client_command_status_message(status));
548 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
549 silc_client_command_reply_free(cmd);
553 argc = silc_argument_get_arg_num(cmd->args);
554 if (argc < 1 || argc > 3) {
559 /* Take Channel ID */
560 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
565 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
569 channel_id = silc_id_payload_parse_id(tmp, len);
573 /* Get the channel name */
574 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
575 SILC_ID_CHANNEL, &id_cache)) {
576 silc_free(channel_id);
581 channel = (SilcChannelEntry)id_cache->context;
583 cmd->client->ops->say(cmd->client, conn,
584 "Topic on channel %s: %s", channel->channel_name,
587 /* Notify application */
588 COMMAND_REPLY((ARGS, channel, topic));
590 /* Execute any pending command callbacks */
591 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
594 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
595 silc_client_command_reply_free(cmd);
598 /* Received reply to invite command. */
600 SILC_CLIENT_CMD_REPLY_FUNC(invite)
602 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
603 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
604 SilcCommandStatus status;
607 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
608 SILC_GET16_MSB(status, tmp);
609 if (status != SILC_STATUS_OK) {
610 cmd->client->ops->say(cmd->client, conn,
611 "%s", silc_client_command_status_message(status));
613 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
614 silc_client_command_reply_free(cmd);
618 /* Notify application */
619 COMMAND_REPLY((ARGS));
621 /* Execute any pending command callbacks */
622 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
624 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
625 silc_client_command_reply_free(cmd);
628 SILC_CLIENT_CMD_REPLY_FUNC(kill)
632 /* Received reply to INFO command. We receive the server ID and some
633 information about the server user requested. */
635 SILC_CLIENT_CMD_REPLY_FUNC(info)
637 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
638 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
639 SilcClient client = cmd->client;
640 SilcCommandStatus status;
643 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
644 SILC_GET16_MSB(status, tmp);
645 if (status != SILC_STATUS_OK) {
646 cmd->client->ops->say(cmd->client, conn,
647 "%s", silc_client_command_status_message(status));
649 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
650 silc_client_command_reply_free(cmd);
655 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
659 /* XXX save server id */
661 /* Get server info */
662 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
666 client->ops->say(cmd->client, conn, "Info: %s", tmp);
668 /* Notify application */
669 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
671 /* Execute any pending command callbacks */
672 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
675 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
676 silc_client_command_reply_free(cmd);
679 /* Received reply to PING command. The reply time is shown to user. */
681 SILC_CLIENT_CMD_REPLY_FUNC(ping)
683 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
684 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
685 SilcCommandStatus status;
688 time_t diff, curtime;
690 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
691 if (status != SILC_STATUS_OK) {
692 cmd->client->ops->say(cmd->client, conn,
693 "%s", silc_client_command_status_message(status));
698 curtime = time(NULL);
699 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
700 cmd->packet->src_id_type);
706 for (i = 0; i < conn->ping_count; i++) {
707 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
708 diff = curtime - conn->ping[i].start_time;
709 cmd->client->ops->say(cmd->client, conn,
710 "Ping reply from %s: %d second%s",
711 conn->ping[i].dest_name, diff,
712 diff == 1 ? "" : "s");
714 conn->ping[i].start_time = 0;
715 silc_free(conn->ping[i].dest_id);
716 conn->ping[i].dest_id = NULL;
717 silc_free(conn->ping[i].dest_name);
718 conn->ping[i].dest_name = NULL;
725 /* Notify application */
726 COMMAND_REPLY((ARGS));
728 /* Execute any pending command callbacks */
729 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
732 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
733 silc_client_command_reply_free(cmd);
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_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
835 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
836 silc_client_command_reply_free(cmd);
839 /* Received reply for MOTD command */
841 SILC_CLIENT_CMD_REPLY_FUNC(motd)
843 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
844 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
845 SilcCommandStatus status;
846 unsigned int argc, i;
848 char *motd = NULL, *cp, line[256];
850 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
851 SILC_GET16_MSB(status, tmp);
852 if (status != SILC_STATUS_OK) {
853 cmd->client->ops->say(cmd->client, conn,
854 "%s", silc_client_command_status_message(status));
859 argc = silc_argument_get_arg_num(cmd->args);
866 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
875 if (cp[i++] == '\n') {
876 memset(line, 0, sizeof(line));
877 strncat(line, cp, i - 1);
883 cmd->client->ops->say(cmd->client, conn, "%s", line);
892 /* Notify application */
893 COMMAND_REPLY((ARGS, motd));
895 /* Execute any pending command callbacks */
896 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
899 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
900 silc_client_command_reply_free(cmd);
903 SILC_CLIENT_CMD_REPLY_FUNC(umode)
907 /* Received reply for CMODE command. */
909 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
911 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
912 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
913 SilcCommandStatus status;
916 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
917 if (status != SILC_STATUS_OK) {
918 cmd->client->ops->say(cmd->client, conn,
919 "%s", silc_client_command_status_message(status));
924 /* Get channel mode */
925 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
931 /* Notify application */
932 COMMAND_REPLY((ARGS, tmp));
934 /* Execute any pending command callbacks */
935 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
938 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
939 silc_client_command_reply_free(cmd);
942 /* Received reply for CUMODE command */
944 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
946 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
947 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
948 SilcCommandStatus status;
949 SilcIDCacheEntry id_cache = NULL;
950 SilcClientID *client_id;
951 unsigned char *tmp, *id;
954 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
955 if (status != SILC_STATUS_OK) {
956 cmd->client->ops->say(cmd->client, conn,
957 "%s", silc_client_command_status_message(status));
962 /* Get channel mode */
963 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
970 id = silc_argument_get_arg_type(cmd->args, 3, &len);
975 client_id = silc_id_payload_parse_id(id, len);
981 /* Get client entry */
982 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
983 SILC_ID_CLIENT, &id_cache)) {
988 /* Notify application */
989 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
990 silc_free(client_id);
992 /* Execute any pending command callbacks */
993 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
996 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
997 silc_client_command_reply_free(cmd);
1000 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1002 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1003 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1004 SilcCommandStatus status;
1007 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1008 SILC_GET16_MSB(status, tmp);
1009 if (status != SILC_STATUS_OK) {
1010 cmd->client->ops->say(cmd->client, conn,
1011 "%s", silc_client_command_status_message(status));
1012 COMMAND_REPLY_ERROR;
1016 /* Notify application */
1017 COMMAND_REPLY((ARGS));
1019 /* Execute any pending command callbacks */
1020 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1023 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1024 silc_client_command_reply_free(cmd);
1027 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1031 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1035 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1037 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1038 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1039 SilcCommandStatus status;
1042 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1043 SILC_GET16_MSB(status, tmp);
1044 if (status != SILC_STATUS_OK) {
1045 cmd->client->ops->say(cmd->client, conn,
1046 "%s", silc_client_command_status_message(status));
1047 COMMAND_REPLY_ERROR;
1051 /* Notify application */
1052 COMMAND_REPLY((ARGS));
1054 /* Execute any pending command callbacks */
1055 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1058 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1059 silc_client_command_reply_free(cmd);
1062 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1064 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1065 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1066 SilcCommandStatus status;
1069 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1070 SILC_GET16_MSB(status, tmp);
1071 if (status != SILC_STATUS_OK) {
1072 cmd->client->ops->say(cmd->client, conn,
1073 "%s", silc_client_command_status_message(status));
1074 COMMAND_REPLY_ERROR;
1078 /* Notify application */
1079 COMMAND_REPLY((ARGS));
1081 /* Execute any pending command callbacks */
1082 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1085 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1086 silc_client_command_reply_free(cmd);
1089 SILC_CLIENT_CMD_REPLY_FUNC(close)
1091 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1092 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1093 SilcCommandStatus status;
1096 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1097 SILC_GET16_MSB(status, tmp);
1098 if (status != SILC_STATUS_OK) {
1099 cmd->client->ops->say(cmd->client, conn,
1100 "%s", silc_client_command_status_message(status));
1101 COMMAND_REPLY_ERROR;
1105 /* Notify application */
1106 COMMAND_REPLY((ARGS));
1108 /* Execute any pending command callbacks */
1109 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1112 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1113 silc_client_command_reply_free(cmd);
1116 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1118 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1119 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1120 SilcCommandStatus status;
1123 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1124 SILC_GET16_MSB(status, tmp);
1125 if (status != SILC_STATUS_OK) {
1126 cmd->client->ops->say(cmd->client, conn,
1127 "%s", silc_client_command_status_message(status));
1128 COMMAND_REPLY_ERROR;
1132 /* Notify application */
1133 COMMAND_REPLY((ARGS));
1135 /* Execute any pending command callbacks */
1136 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1139 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1140 silc_client_command_reply_free(cmd);
1143 /* Reply to LEAVE command. */
1145 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1147 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1148 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1149 SilcCommandStatus status;
1152 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1153 SILC_GET16_MSB(status, tmp);
1154 if (status != SILC_STATUS_OK) {
1155 cmd->client->ops->say(cmd->client, conn,
1156 "%s", silc_client_command_status_message(status));
1157 COMMAND_REPLY_ERROR;
1161 /* Notify application */
1162 COMMAND_REPLY((ARGS));
1164 /* Execute any pending command callbacks */
1165 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1168 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1169 silc_client_command_reply_free(cmd);
1172 /* Reply to USERS command. Received list of client ID's and theirs modes
1173 on the channel we requested. */
1175 SILC_CLIENT_CMD_REPLY_FUNC(users)
1177 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1178 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1179 SilcCommandStatus status;
1180 SilcIDCacheEntry id_cache = NULL;
1181 SilcChannelEntry channel;
1182 SilcChannelUser chu;
1183 SilcChannelID *channel_id = NULL;
1184 SilcBuffer client_id_list;
1185 SilcBuffer client_mode_list;
1187 unsigned int tmp_len, list_count;
1189 unsigned char **res_argv = NULL;
1190 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1192 SILC_LOG_DEBUG(("Start"));
1194 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1195 SILC_GET16_MSB(status, tmp);
1196 if (status != SILC_STATUS_OK) {
1197 cmd->client->ops->say(cmd->client, conn,
1198 "%s", silc_client_command_status_message(status));
1199 COMMAND_REPLY_ERROR;
1203 /* Get channel ID */
1204 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1207 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1211 /* Get the list count */
1212 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1215 SILC_GET32_MSB(list_count, tmp);
1217 /* Get Client ID list */
1218 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1222 client_id_list = silc_buffer_alloc(tmp_len);
1223 silc_buffer_pull_tail(client_id_list, tmp_len);
1224 silc_buffer_put(client_id_list, tmp, tmp_len);
1226 /* Get client mode list */
1227 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1231 client_mode_list = silc_buffer_alloc(tmp_len);
1232 silc_buffer_pull_tail(client_mode_list, tmp_len);
1233 silc_buffer_put(client_mode_list, tmp, tmp_len);
1235 /* Get channel entry */
1236 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1237 SILC_ID_CHANNEL, &id_cache)) {
1238 COMMAND_REPLY_ERROR;
1241 channel = (SilcChannelEntry)id_cache->context;
1243 /* Remove old client list from channel. */
1244 silc_list_start(channel->clients);
1245 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1246 silc_list_del(channel->clients, chu);
1250 /* Cache the received Client ID's and modes. This cache expires
1251 whenever server sends notify message to channel. It means two things;
1252 some user has joined or leaved the channel. XXX! */
1253 for (i = 0; i < list_count; i++) {
1254 unsigned short idp_len;
1256 SilcClientID *client_id;
1257 SilcClientEntry client;
1260 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1262 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1267 SILC_GET32_MSB(mode, client_mode_list->data);
1269 /* Check if we have this client cached already. */
1270 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1271 SILC_ID_CLIENT, &id_cache)) {
1272 /* No we don't have it, query it from the server. Assemble argument
1273 table that will be sent fr the IDENTIFY command later. */
1274 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1276 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1278 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1280 res_argv[res_argc] = client_id_list->data;
1281 res_argv_lens[res_argc] = idp_len;
1282 res_argv_types[res_argc] = res_argc + 3;
1285 /* Found the client, join it to the channel */
1286 client = (SilcClientEntry)id_cache->context;
1287 chu = silc_calloc(1, sizeof(*chu));
1288 chu->client = client;
1290 silc_list_add(channel->clients, chu);
1292 silc_free(client_id);
1296 silc_buffer_pull(client_id_list, idp_len);
1297 silc_buffer_pull(client_mode_list, 4);
1300 /* Query the client information from server if the list included clients
1301 that we don't know about. */
1305 /* Send the IDENTIFY command to server */
1306 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1307 res_argc, res_argv, res_argv_lens,
1308 res_argv_types, ++conn->cmd_ident);
1309 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1310 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1313 /* Register pending command callback. After we've received the IDENTIFY
1314 command reply we will reprocess this command reply by re-calling this
1315 USERS command reply callback. */
1316 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1317 NULL, silc_client_command_reply_users, cmd);
1319 silc_buffer_free(res_cmd);
1321 silc_free(channel_id);
1323 silc_free(res_argv);
1324 silc_free(res_argv_lens);
1325 silc_free(res_argv_types);
1329 /* We have all the clients on the channel cached now. Create a nice
1330 output for user interface and notify application. */
1332 if (!cmd->callback) {
1333 /* Server has sent us USERS reply even when we haven't actually sent
1334 USERS command. This is normal behaviour when joining to a channel.
1335 Display some nice information on the user interface. */
1336 int k = 0, len1 = 0, len2 = 0;
1337 char *name_list = NULL;
1339 silc_list_start(channel->clients);
1340 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1341 char *m, *n = chu->client->nickname;
1345 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1347 m = silc_client_chumode_char(chu->mode);
1349 memcpy(name_list + (len1 - len2), m, strlen(m));
1354 memcpy(name_list + (len1 - len2), n, len2);
1355 name_list[len1] = 0;
1357 if (k == silc_list_count(channel->clients) - 1)
1359 memcpy(name_list + len1, " ", 1);
1364 cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1365 channel->channel_name, name_list);
1366 silc_free(name_list);
1369 /* Notify application */
1370 COMMAND_REPLY((ARGS, channel, client_id_list->head,
1371 client_mode_list->head));
1373 /* Execute any pending command callbacks */
1374 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1376 silc_buffer_free(client_id_list);
1377 silc_buffer_free(client_mode_list);
1381 silc_free(channel_id);
1382 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1383 silc_client_command_reply_free(cmd);