5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 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"
37 #include "client_internal.h"
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
42 SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43 SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44 SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45 SILC_CLIENT_CMD_REPLY(nick, NICK),
46 SILC_CLIENT_CMD_REPLY(list, LIST),
47 SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48 SILC_CLIENT_CMD_REPLY(invite, INVITE),
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(shutdown, SHUTDOWN),
63 SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64 SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65 SILC_CLIENT_CMD_REPLY(users, USERS),
70 const SilcCommandStatusMessage silc_command_status_messages[] = {
72 { STAT(NO_SUCH_NICK), "No such nickname" },
73 { STAT(NO_SUCH_CHANNEL), "No such channel" },
74 { STAT(NO_SUCH_SERVER), "No such server" },
75 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
76 { STAT(NO_RECIPIENT), "No recipient given" },
77 { STAT(UNKNOWN_COMMAND), "Unknown command" },
78 { STAT(WILDCARDS), "Unknown command" },
79 { STAT(NO_CLIENT_ID), "No Client ID given" },
80 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
81 { STAT(NO_SERVER_ID), "No Server ID given" },
82 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
83 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
84 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
85 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
86 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
87 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
88 { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
89 { STAT(USER_ON_CHANNEL), "User already on the channel" },
90 { STAT(NOT_REGISTERED), "You have not registered" },
91 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
92 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
93 { STAT(PERM_DENIED), "Your host is not among the privileged" },
94 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
95 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
96 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
97 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
98 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
99 { STAT(UNKNOWN_MODE), "Unknown mode" },
100 { STAT(NOT_YOU), "Cannot change mode for other users" },
101 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
102 { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
103 { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" },
104 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
105 { STAT(BAD_NICKNAME), "Bad nickname" },
106 { STAT(BAD_CHANNEL), "Bad channel name" },
107 { STAT(AUTH_FAILED), "Authentication failed" },
108 { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
112 /* Command reply operation that is called at the end of all command replys.
113 Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
114 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
115 #define ARGS cmd->client, cmd->sock->user_data, \
116 cmd->payload, TRUE, silc_command_get(cmd->payload), status
118 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
119 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
120 cmd->sock->user_data, cmd->payload, FALSE, \
121 silc_command_get(cmd->payload), status)
123 /* Process received command reply. */
125 void silc_client_command_reply_process(SilcClient client,
126 SilcSocketConnection sock,
127 SilcPacketContext *packet)
129 SilcBuffer buffer = packet->buffer;
130 SilcClientCommandReply *cmd;
131 SilcClientCommandReplyContext ctx;
132 SilcCommandPayload payload;
134 unsigned short ident;
136 /* Get command reply payload from packet */
137 payload = silc_command_payload_parse(buffer);
139 /* Silently ignore bad reply packet */
140 SILC_LOG_DEBUG(("Bad command reply packet"));
144 /* Allocate command reply context. This must be free'd by the
145 command reply routine receiving it. */
146 ctx = silc_calloc(1, sizeof(*ctx));
147 ctx->client = client;
149 ctx->payload = payload;
150 ctx->args = silc_command_get_args(ctx->payload);
151 ctx->packet = packet;
152 ident = silc_command_get_ident(ctx->payload);
154 /* Check for pending commands and mark to be exeucted */
155 silc_client_command_pending_check(sock->user_data, ctx,
156 silc_command_get(ctx->payload), ident);
158 /* Execute command reply */
159 command = silc_command_get(ctx->payload);
160 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
161 if (cmd->cmd == command)
164 if (cmd == NULL || !cmd->cb) {
172 /* Returns status message string */
174 char *silc_client_command_status_message(SilcCommandStatus status)
178 for (i = 0; silc_command_status_messages[i].message; i++) {
179 if (silc_command_status_messages[i].status == status)
183 if (silc_command_status_messages[i].message == NULL)
186 return silc_command_status_messages[i].message;
189 /* Free command reply context and its internals. */
191 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
194 silc_command_free_payload(cmd->payload);
200 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
201 SilcCommandStatus status)
203 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
204 SilcClientID *client_id;
205 SilcIDCacheEntry id_cache = NULL;
206 SilcClientEntry client_entry = NULL;
208 unsigned char *id_data, *tmp;
209 char *nickname = NULL, *username = NULL;
210 char *realname = NULL;
211 unsigned int idle = 0;
213 argc = silc_argument_get_arg_num(cmd->args);
215 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
221 client_id = silc_id_payload_parse_id(id_data, len);
227 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
228 username = silc_argument_get_arg_type(cmd->args, 4, &len);
229 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
230 if (!nickname || !username || !realname) {
235 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
237 SILC_GET32_MSB(idle, tmp);
239 /* Check if we have this client cached already. */
240 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
241 SILC_ID_CLIENT, &id_cache)) {
242 SILC_LOG_DEBUG(("Adding new client entry"));
244 client_entry = silc_calloc(1, sizeof(*client_entry));
245 client_entry->id = client_id;
246 silc_parse_nickname(nickname, &client_entry->nickname,
247 &client_entry->server, &client_entry->num);
248 client_entry->username = strdup(username);
250 client_entry->realname = strdup(realname);
252 /* Add client to cache */
253 silc_idcache_add(conn->client_cache, client_entry->nickname,
254 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
256 client_entry = (SilcClientEntry)id_cache->context;
257 if (client_entry->nickname)
258 silc_free(client_entry->nickname);
259 if (client_entry->server)
260 silc_free(client_entry->server);
261 if (client_entry->username)
262 silc_free(client_entry->username);
263 if (client_entry->realname)
264 silc_free(client_entry->realname);
266 SILC_LOG_DEBUG(("Updating client entry"));
268 silc_parse_nickname(nickname, &client_entry->nickname,
269 &client_entry->server, &client_entry->num);
270 client_entry->username = strdup(username);
272 client_entry->realname = strdup(realname);
274 id_cache->data = client_entry->nickname;
275 silc_idcache_sort_by_data(conn->client_cache);
277 silc_free(client_id);
280 /* Notify application */
282 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
286 /* Received reply for WHOIS command. This maybe called several times
287 for one WHOIS command as server may reply with list of results. */
289 SILC_CLIENT_CMD_REPLY_FUNC(whois)
291 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
292 SilcCommandStatus status;
295 SILC_LOG_DEBUG(("Start"));
297 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
298 SILC_GET16_MSB(status, tmp);
299 if (status != SILC_STATUS_OK &&
300 status != SILC_STATUS_LIST_START &&
301 status != SILC_STATUS_LIST_ITEM &&
302 status != SILC_STATUS_LIST_END) {
307 /* Display one whois reply */
308 if (status == SILC_STATUS_OK)
309 silc_client_command_reply_whois_save(cmd, status);
312 if (status == SILC_STATUS_LIST_START ||
313 status == SILC_STATUS_LIST_ITEM ||
314 status == SILC_STATUS_LIST_END)
315 silc_client_command_reply_whois_save(cmd, status);
317 /* Pending callbacks are not executed if this was an list entry */
318 if (status != SILC_STATUS_OK &&
319 status != SILC_STATUS_LIST_END) {
320 silc_client_command_reply_free(cmd);
324 /* Execute any pending command callbacks */
325 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
328 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
329 silc_client_command_reply_free(cmd);
332 /* Received reply for WHOWAS command. */
334 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
340 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
341 SilcCommandStatus status)
343 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
344 SilcClientID *client_id;
345 SilcIDCacheEntry id_cache = NULL;
346 SilcClientEntry client_entry = NULL;
348 unsigned char *id_data;
349 char *nickname = NULL, *username = NULL;
351 argc = silc_argument_get_arg_num(cmd->args);
353 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
359 client_id = silc_id_payload_parse_id(id_data, len);
365 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
366 username = silc_argument_get_arg_type(cmd->args, 4, &len);
368 /* Check if we have this client cached already. */
369 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
370 SILC_ID_CLIENT, &id_cache)) {
371 SILC_LOG_DEBUG(("Adding new client entry"));
373 client_entry = silc_calloc(1, sizeof(*client_entry));
374 client_entry->id = client_id;
375 silc_parse_nickname(nickname, &client_entry->nickname,
376 &client_entry->server, &client_entry->num);
378 client_entry->username = strdup(username);
380 /* Add client to cache */
381 silc_idcache_add(conn->client_cache, client_entry->nickname,
382 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
384 client_entry = (SilcClientEntry)id_cache->context;
385 if (client_entry->nickname)
386 silc_free(client_entry->nickname);
387 if (client_entry->server)
388 silc_free(client_entry->server);
389 if (username && client_entry->username)
390 silc_free(client_entry->username);
392 SILC_LOG_DEBUG(("Updating client entry"));
394 silc_parse_nickname(nickname, &client_entry->nickname,
395 &client_entry->server, &client_entry->num);
398 client_entry->username = strdup(username);
400 id_cache->data = client_entry->nickname;
401 silc_idcache_sort_by_data(conn->client_cache);
403 silc_free(client_id);
406 /* Notify application */
407 COMMAND_REPLY((ARGS, client_entry, nickname, username));
410 /* Received reply for IDENTIFY command. This maybe called several times
411 for one IDENTIFY command as server may reply with list of results.
412 This is totally silent and does not print anything on screen. */
414 SILC_CLIENT_CMD_REPLY_FUNC(identify)
416 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
417 SilcCommandStatus status;
420 SILC_LOG_DEBUG(("Start"));
422 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
423 SILC_GET16_MSB(status, tmp);
424 if (status != SILC_STATUS_OK &&
425 status != SILC_STATUS_LIST_START &&
426 status != SILC_STATUS_LIST_ITEM &&
427 status != SILC_STATUS_LIST_END) {
432 /* Save one IDENTIFY entry */
433 if (status == SILC_STATUS_OK)
434 silc_client_command_reply_identify_save(cmd, status);
437 if (status == SILC_STATUS_LIST_START ||
438 status == SILC_STATUS_LIST_ITEM ||
439 status == SILC_STATUS_LIST_END)
440 silc_client_command_reply_identify_save(cmd, status);
442 /* Pending callbacks are not executed if this was an list entry */
443 if (status != SILC_STATUS_OK &&
444 status != SILC_STATUS_LIST_END) {
445 silc_client_command_reply_free(cmd);
449 /* Execute any pending command callbacks */
450 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
453 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
454 silc_client_command_reply_free(cmd);
457 /* Received reply for command NICK. If everything went without errors
458 we just received our new Client ID. */
460 SILC_CLIENT_CMD_REPLY_FUNC(nick)
462 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
463 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
464 SilcCommandStatus status;
467 unsigned int argc, len;
469 SILC_LOG_DEBUG(("Start"));
471 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
472 if (status != SILC_STATUS_OK) {
473 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
474 silc_client_command_status_message(status));
479 argc = silc_argument_get_arg_num(cmd->args);
480 if (argc < 2 || argc > 2) {
481 cmd->client->ops->say(cmd->client, conn,
482 "Cannot set nickname: bad reply to command");
487 /* Take received Client ID */
488 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
489 idp = silc_id_payload_parse_data(tmp, len);
494 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
496 /* Notify application */
497 COMMAND_REPLY((ARGS, conn->local_entry));
499 /* Execute any pending command callbacks */
500 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
503 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
504 silc_client_command_reply_free(cmd);
507 SILC_CLIENT_CMD_REPLY_FUNC(list)
511 /* Received reply to topic command. */
513 SILC_CLIENT_CMD_REPLY_FUNC(topic)
515 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
516 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
517 SilcCommandStatus status;
518 SilcChannelEntry channel;
519 SilcChannelID *channel_id = NULL;
520 SilcIDCacheEntry id_cache = NULL;
523 unsigned int argc, len;
525 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
526 if (status != SILC_STATUS_OK) {
527 cmd->client->ops->say(cmd->client, conn,
528 "%s", silc_client_command_status_message(status));
530 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
531 silc_client_command_reply_free(cmd);
535 argc = silc_argument_get_arg_num(cmd->args);
536 if (argc < 1 || argc > 3) {
541 /* Take Channel ID */
542 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
547 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
551 channel_id = silc_id_payload_parse_id(tmp, len);
555 /* Get the channel name */
556 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
557 SILC_ID_CHANNEL, &id_cache)) {
558 silc_free(channel_id);
563 channel = (SilcChannelEntry)id_cache->context;
565 cmd->client->ops->say(cmd->client, conn,
566 "Topic on channel %s: %s", channel->channel_name,
569 /* Notify application */
570 COMMAND_REPLY((ARGS, channel, topic));
572 /* Execute any pending command callbacks */
573 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
576 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
577 silc_client_command_reply_free(cmd);
580 /* Received reply to invite command. */
582 SILC_CLIENT_CMD_REPLY_FUNC(invite)
584 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
585 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
586 SilcCommandStatus status;
589 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
590 SILC_GET16_MSB(status, tmp);
591 if (status != SILC_STATUS_OK) {
592 cmd->client->ops->say(cmd->client, conn,
593 "%s", silc_client_command_status_message(status));
595 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
596 silc_client_command_reply_free(cmd);
600 /* Notify application */
601 COMMAND_REPLY((ARGS));
603 /* Execute any pending command callbacks */
604 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
606 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
607 silc_client_command_reply_free(cmd);
610 SILC_CLIENT_CMD_REPLY_FUNC(kill)
614 /* Received reply to INFO command. We receive the server ID and some
615 information about the server user requested. */
617 SILC_CLIENT_CMD_REPLY_FUNC(info)
619 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
620 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
621 SilcClient client = cmd->client;
622 SilcCommandStatus status;
625 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
626 SILC_GET16_MSB(status, tmp);
627 if (status != SILC_STATUS_OK) {
628 cmd->client->ops->say(cmd->client, conn,
629 "%s", silc_client_command_status_message(status));
631 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
632 silc_client_command_reply_free(cmd);
637 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
641 /* XXX save server id */
643 /* Get server info */
644 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
648 client->ops->say(cmd->client, conn, "Info: %s", tmp);
650 /* Notify application */
651 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
653 /* Execute any pending command callbacks */
654 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
657 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
658 silc_client_command_reply_free(cmd);
661 /* Received reply to PING command. The reply time is shown to user. */
663 SILC_CLIENT_CMD_REPLY_FUNC(ping)
665 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
666 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
667 SilcCommandStatus status;
670 time_t diff, curtime;
672 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
673 if (status != SILC_STATUS_OK) {
674 cmd->client->ops->say(cmd->client, conn,
675 "%s", silc_client_command_status_message(status));
680 curtime = time(NULL);
681 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
682 cmd->packet->src_id_type);
688 for (i = 0; i < conn->ping_count; i++) {
689 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
690 diff = curtime - conn->ping[i].start_time;
691 cmd->client->ops->say(cmd->client, conn,
692 "Ping reply from %s: %d second%s",
693 conn->ping[i].dest_name, diff,
694 diff == 1 ? "" : "s");
696 conn->ping[i].start_time = 0;
697 silc_free(conn->ping[i].dest_id);
698 conn->ping[i].dest_id = NULL;
699 silc_free(conn->ping[i].dest_name);
700 conn->ping[i].dest_name = NULL;
707 /* Notify application */
708 COMMAND_REPLY((ARGS));
710 /* Execute any pending command callbacks */
711 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
714 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
715 silc_client_command_reply_free(cmd);
718 /* Received reply for JOIN command. */
720 SILC_CLIENT_CMD_REPLY_FUNC(join)
722 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
723 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
724 SilcCommandStatus status;
725 SilcIDPayload idp = NULL;
726 SilcChannelEntry channel;
727 SilcIDCacheEntry id_cache = NULL;
729 unsigned int argc, mode, len, list_count;
730 char *topic, *tmp, *channel_name = NULL, *hmac;
731 SilcBuffer keyp, client_id_list, client_mode_list;
734 SILC_LOG_DEBUG(("Start"));
736 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
737 if (status != SILC_STATUS_OK) {
738 cmd->client->ops->say(cmd->client, conn,
739 "%s", silc_client_command_status_message(status));
744 argc = silc_argument_get_arg_num(cmd->args);
745 if (argc < 7 || argc > 14) {
746 cmd->client->ops->say(cmd->client, conn,
747 "Cannot join channel: Bad reply packet");
752 /* Get channel name */
753 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
755 cmd->client->ops->say(cmd->client, conn,
756 "Cannot join channel: Bad reply packet");
760 channel_name = strdup(tmp);
763 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
765 cmd->client->ops->say(cmd->client, conn,
766 "Cannot join channel: Bad reply packet");
768 silc_free(channel_name);
771 idp = silc_id_payload_parse_data(tmp, len);
774 silc_free(channel_name);
778 /* Get channel mode */
779 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
781 SILC_GET32_MSB(mode, tmp);
785 /* Get channel key */
786 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
788 silc_id_payload_free(idp);
789 silc_free(channel_name);
792 keyp = silc_buffer_alloc(len);
793 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
794 silc_buffer_put(keyp, tmp, len);
797 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
799 /* Save received Channel ID. This actually creates the channel */
800 channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
802 silc_id_payload_free(idp);
805 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
807 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
808 cmd->client->ops->say(cmd->client, conn,
809 "Cannot join channel: Unsupported HMAC `%s'",
812 silc_free(channel_name);
817 /* Get the list count */
818 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
821 SILC_GET32_MSB(list_count, tmp);
823 /* Get Client ID list */
824 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
828 client_id_list = silc_buffer_alloc(len);
829 silc_buffer_pull_tail(client_id_list, len);
830 silc_buffer_put(client_id_list, tmp, len);
832 /* Get client mode list */
833 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
837 client_mode_list = silc_buffer_alloc(len);
838 silc_buffer_pull_tail(client_mode_list, len);
839 silc_buffer_put(client_mode_list, tmp, len);
841 /* Add clients we received in the reply to the channel */
842 for (i = 0; i < list_count; i++) {
843 unsigned short idp_len;
845 SilcClientID *client_id;
846 SilcClientEntry client_entry;
849 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
851 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
856 SILC_GET32_MSB(mode, client_mode_list->data);
858 /* Check if we have this client cached already. */
859 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
860 SILC_ID_CLIENT, &id_cache)) {
861 /* No, we don't have it, add entry for it. */
862 client_entry = silc_calloc(1, sizeof(*client_entry));
863 client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
864 silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT,
865 client_entry->id, (void *)client_entry, FALSE);
867 /* Yes, we have it already */
868 client_entry = (SilcClientEntry)id_cache->context;
871 /* Join the client to the channel */
872 chu = silc_calloc(1, sizeof(*chu));
873 chu->client = client_entry;
875 silc_list_add(channel->clients, chu);
876 silc_free(client_id);
878 silc_buffer_pull(client_id_list, idp_len);
879 silc_buffer_pull(client_mode_list, 4);
881 silc_buffer_push(client_id_list, client_id_list->data -
882 client_id_list->head);
883 silc_buffer_push(client_mode_list, client_mode_list->data -
884 client_mode_list->head);
886 /* Save channel key */
887 silc_client_save_channel_key(conn, keyp, channel);
889 /* Notify application */
890 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
891 NULL, topic, hmac, list_count, client_id_list,
894 /* Execute any pending command callbacks */
895 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
897 silc_buffer_free(keyp);
898 silc_buffer_free(client_id_list);
899 silc_buffer_free(client_mode_list);
902 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
903 silc_client_command_reply_free(cmd);
906 /* Received reply for MOTD command */
908 SILC_CLIENT_CMD_REPLY_FUNC(motd)
910 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
911 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
912 SilcCommandStatus status;
913 unsigned int argc, i;
915 char *motd = NULL, *cp, line[256];
917 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
918 SILC_GET16_MSB(status, tmp);
919 if (status != SILC_STATUS_OK) {
920 cmd->client->ops->say(cmd->client, conn,
921 "%s", silc_client_command_status_message(status));
926 argc = silc_argument_get_arg_num(cmd->args);
933 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
942 if (cp[i++] == '\n') {
943 memset(line, 0, sizeof(line));
944 strncat(line, cp, i - 1);
950 cmd->client->ops->say(cmd->client, conn, "%s", line);
959 /* Notify application */
960 COMMAND_REPLY((ARGS, motd));
962 /* Execute any pending command callbacks */
963 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
966 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
967 silc_client_command_reply_free(cmd);
970 SILC_CLIENT_CMD_REPLY_FUNC(umode)
974 /* Received reply for CMODE command. */
976 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
978 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
979 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
980 SilcCommandStatus status;
983 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
984 if (status != SILC_STATUS_OK) {
985 cmd->client->ops->say(cmd->client, conn,
986 "%s", silc_client_command_status_message(status));
991 /* Get channel mode */
992 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
998 /* Notify application */
999 COMMAND_REPLY((ARGS, tmp));
1001 /* Execute any pending command callbacks */
1002 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1005 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1006 silc_client_command_reply_free(cmd);
1009 /* Received reply for CUMODE command */
1011 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1013 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1014 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1015 SilcCommandStatus status;
1016 SilcIDCacheEntry id_cache = NULL;
1017 SilcClientID *client_id;
1018 unsigned char *tmp, *id;
1021 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1022 if (status != SILC_STATUS_OK) {
1023 cmd->client->ops->say(cmd->client, conn,
1024 "%s", silc_client_command_status_message(status));
1025 COMMAND_REPLY_ERROR;
1029 /* Get channel mode */
1030 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1032 COMMAND_REPLY_ERROR;
1037 id = silc_argument_get_arg_type(cmd->args, 3, &len);
1039 COMMAND_REPLY_ERROR;
1042 client_id = silc_id_payload_parse_id(id, len);
1044 COMMAND_REPLY_ERROR;
1048 /* Get client entry */
1049 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1050 SILC_ID_CLIENT, &id_cache)) {
1051 COMMAND_REPLY_ERROR;
1055 /* Notify application */
1056 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1057 silc_free(client_id);
1059 /* Execute any pending command callbacks */
1060 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1063 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1064 silc_client_command_reply_free(cmd);
1067 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1069 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1070 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1071 SilcCommandStatus status;
1074 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1075 SILC_GET16_MSB(status, tmp);
1076 if (status != SILC_STATUS_OK) {
1077 cmd->client->ops->say(cmd->client, conn,
1078 "%s", silc_client_command_status_message(status));
1079 COMMAND_REPLY_ERROR;
1083 /* Notify application */
1084 COMMAND_REPLY((ARGS));
1086 /* Execute any pending command callbacks */
1087 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1090 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1091 silc_client_command_reply_free(cmd);
1094 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1098 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1102 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1104 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1105 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1106 SilcCommandStatus status;
1109 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1110 SILC_GET16_MSB(status, tmp);
1111 if (status != SILC_STATUS_OK) {
1112 cmd->client->ops->say(cmd->client, conn,
1113 "%s", silc_client_command_status_message(status));
1114 COMMAND_REPLY_ERROR;
1118 /* Notify application */
1119 COMMAND_REPLY((ARGS));
1121 /* Execute any pending command callbacks */
1122 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1125 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1126 silc_client_command_reply_free(cmd);
1129 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1131 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1132 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1133 SilcCommandStatus status;
1136 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1137 SILC_GET16_MSB(status, tmp);
1138 if (status != SILC_STATUS_OK) {
1139 cmd->client->ops->say(cmd->client, conn,
1140 "%s", silc_client_command_status_message(status));
1141 COMMAND_REPLY_ERROR;
1145 /* Notify application */
1146 COMMAND_REPLY((ARGS));
1148 /* Execute any pending command callbacks */
1149 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1152 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1153 silc_client_command_reply_free(cmd);
1156 SILC_CLIENT_CMD_REPLY_FUNC(close)
1158 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1159 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1160 SilcCommandStatus status;
1163 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1164 SILC_GET16_MSB(status, tmp);
1165 if (status != SILC_STATUS_OK) {
1166 cmd->client->ops->say(cmd->client, conn,
1167 "%s", silc_client_command_status_message(status));
1168 COMMAND_REPLY_ERROR;
1172 /* Notify application */
1173 COMMAND_REPLY((ARGS));
1175 /* Execute any pending command callbacks */
1176 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1179 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1180 silc_client_command_reply_free(cmd);
1183 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1185 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1186 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1187 SilcCommandStatus status;
1190 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1191 SILC_GET16_MSB(status, tmp);
1192 if (status != SILC_STATUS_OK) {
1193 cmd->client->ops->say(cmd->client, conn,
1194 "%s", silc_client_command_status_message(status));
1195 COMMAND_REPLY_ERROR;
1199 /* Notify application */
1200 COMMAND_REPLY((ARGS));
1202 /* Execute any pending command callbacks */
1203 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1206 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1207 silc_client_command_reply_free(cmd);
1210 /* Reply to LEAVE command. */
1212 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1214 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1215 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1216 SilcCommandStatus status;
1219 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1220 SILC_GET16_MSB(status, tmp);
1221 if (status != SILC_STATUS_OK) {
1222 cmd->client->ops->say(cmd->client, conn,
1223 "%s", silc_client_command_status_message(status));
1224 COMMAND_REPLY_ERROR;
1228 /* Notify application */
1229 COMMAND_REPLY((ARGS));
1231 /* Execute any pending command callbacks */
1232 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1235 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1236 silc_client_command_reply_free(cmd);
1239 /* Reply to USERS command. Received list of client ID's and theirs modes
1240 on the channel we requested. */
1242 SILC_CLIENT_CMD_REPLY_FUNC(users)
1244 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1245 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1246 SilcCommandStatus status;
1247 SilcIDCacheEntry id_cache = NULL;
1248 SilcChannelEntry channel;
1249 SilcChannelUser chu;
1250 SilcChannelID *channel_id = NULL;
1251 SilcBuffer client_id_list;
1252 SilcBuffer client_mode_list;
1254 unsigned int tmp_len, list_count;
1256 unsigned char **res_argv = NULL;
1257 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1259 SILC_LOG_DEBUG(("Start"));
1261 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1262 SILC_GET16_MSB(status, tmp);
1263 if (status != SILC_STATUS_OK) {
1264 cmd->client->ops->say(cmd->client, conn,
1265 "%s", silc_client_command_status_message(status));
1266 COMMAND_REPLY_ERROR;
1270 /* Get channel ID */
1271 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1274 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1278 /* Get the list count */
1279 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1282 SILC_GET32_MSB(list_count, tmp);
1284 /* Get Client ID list */
1285 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1289 client_id_list = silc_buffer_alloc(tmp_len);
1290 silc_buffer_pull_tail(client_id_list, tmp_len);
1291 silc_buffer_put(client_id_list, tmp, tmp_len);
1293 /* Get client mode list */
1294 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1298 client_mode_list = silc_buffer_alloc(tmp_len);
1299 silc_buffer_pull_tail(client_mode_list, tmp_len);
1300 silc_buffer_put(client_mode_list, tmp, tmp_len);
1302 /* Get channel entry */
1303 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1304 SILC_ID_CHANNEL, &id_cache)) {
1305 COMMAND_REPLY_ERROR;
1308 channel = (SilcChannelEntry)id_cache->context;
1310 /* Remove old client list from channel. */
1311 silc_list_start(channel->clients);
1312 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1313 silc_list_del(channel->clients, chu);
1317 /* Cache the received Client ID's and modes. This cache expires
1318 whenever server sends notify message to channel. It means two things;
1319 some user has joined or leaved the channel. XXX! */
1320 for (i = 0; i < list_count; i++) {
1321 unsigned short idp_len;
1323 SilcClientID *client_id;
1324 SilcClientEntry client;
1327 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1329 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1334 SILC_GET32_MSB(mode, client_mode_list->data);
1336 /* Check if we have this client cached already. */
1337 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1338 SILC_ID_CLIENT, &id_cache)) {
1339 /* No we don't have it, query it from the server. Assemble argument
1340 table that will be sent fr the IDENTIFY command later. */
1341 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1343 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1345 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1347 res_argv[res_argc] = client_id_list->data;
1348 res_argv_lens[res_argc] = idp_len;
1349 res_argv_types[res_argc] = res_argc + 3;
1352 /* Found the client, join it to the channel */
1353 client = (SilcClientEntry)id_cache->context;
1354 chu = silc_calloc(1, sizeof(*chu));
1355 chu->client = client;
1357 silc_list_add(channel->clients, chu);
1359 silc_free(client_id);
1363 silc_buffer_pull(client_id_list, idp_len);
1364 silc_buffer_pull(client_mode_list, 4);
1367 /* Query the client information from server if the list included clients
1368 that we don't know about. */
1372 /* Send the IDENTIFY command to server */
1373 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1374 res_argc, res_argv, res_argv_lens,
1375 res_argv_types, ++conn->cmd_ident);
1376 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1377 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1380 /* Register pending command callback. After we've received the IDENTIFY
1381 command reply we will reprocess this command reply by re-calling this
1382 USERS command reply callback. */
1383 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1384 NULL, silc_client_command_reply_users, cmd);
1386 silc_buffer_free(res_cmd);
1388 silc_free(channel_id);
1390 silc_free(res_argv);
1391 silc_free(res_argv_lens);
1392 silc_free(res_argv_types);
1396 /* Notify application */
1397 COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1399 /* Execute any pending command callbacks */
1400 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1402 silc_buffer_free(client_id_list);
1403 silc_buffer_free(client_mode_list);
1407 silc_free(channel_id);
1408 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1409 silc_client_command_reply_free(cmd);