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,
257 client_entry = (SilcClientEntry)id_cache->context;
258 if (client_entry->nickname)
259 silc_free(client_entry->nickname);
260 if (client_entry->server)
261 silc_free(client_entry->server);
262 if (client_entry->username)
263 silc_free(client_entry->username);
264 if (client_entry->realname)
265 silc_free(client_entry->realname);
267 SILC_LOG_DEBUG(("Updating client entry"));
269 silc_parse_nickname(nickname, &client_entry->nickname,
270 &client_entry->server, &client_entry->num);
271 client_entry->username = strdup(username);
273 client_entry->realname = strdup(realname);
275 id_cache->data = client_entry->nickname;
276 silc_idcache_sort_by_data(conn->client_cache);
278 silc_free(client_id);
281 /* Notify application */
283 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
287 /* Received reply for WHOIS command. This maybe called several times
288 for one WHOIS command as server may reply with list of results. */
290 SILC_CLIENT_CMD_REPLY_FUNC(whois)
292 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
293 SilcCommandStatus status;
296 SILC_LOG_DEBUG(("Start"));
298 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
299 SILC_GET16_MSB(status, tmp);
300 if (status != SILC_STATUS_OK &&
301 status != SILC_STATUS_LIST_START &&
302 status != SILC_STATUS_LIST_ITEM &&
303 status != SILC_STATUS_LIST_END) {
308 /* Display one whois reply */
309 if (status == SILC_STATUS_OK)
310 silc_client_command_reply_whois_save(cmd, status);
313 if (status == SILC_STATUS_LIST_START ||
314 status == SILC_STATUS_LIST_ITEM ||
315 status == SILC_STATUS_LIST_END)
316 silc_client_command_reply_whois_save(cmd, status);
318 /* Pending callbacks are not executed if this was an list entry */
319 if (status != SILC_STATUS_OK &&
320 status != SILC_STATUS_LIST_END) {
321 silc_client_command_reply_free(cmd);
325 /* Execute any pending command callbacks */
326 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
329 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
330 silc_client_command_reply_free(cmd);
333 /* Received reply for WHOWAS command. */
335 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
341 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
342 SilcCommandStatus status)
344 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
345 SilcClientID *client_id;
346 SilcIDCacheEntry id_cache = NULL;
347 SilcClientEntry client_entry = NULL;
349 unsigned char *id_data;
350 char *nickname = NULL, *username = NULL;
352 argc = silc_argument_get_arg_num(cmd->args);
354 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
360 client_id = silc_id_payload_parse_id(id_data, len);
366 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
367 username = silc_argument_get_arg_type(cmd->args, 4, &len);
369 /* Check if we have this client cached already. */
370 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
371 SILC_ID_CLIENT, &id_cache)) {
372 SILC_LOG_DEBUG(("Adding new client entry"));
374 client_entry = silc_calloc(1, sizeof(*client_entry));
375 client_entry->id = client_id;
376 silc_parse_nickname(nickname, &client_entry->nickname,
377 &client_entry->server, &client_entry->num);
379 client_entry->username = strdup(username);
381 /* Add client to cache */
382 silc_idcache_add(conn->client_cache, client_entry->nickname,
383 SILC_ID_CLIENT, client_id, (void *)client_entry,
386 client_entry = (SilcClientEntry)id_cache->context;
387 if (client_entry->nickname)
388 silc_free(client_entry->nickname);
389 if (client_entry->server)
390 silc_free(client_entry->server);
391 if (username && client_entry->username)
392 silc_free(client_entry->username);
394 SILC_LOG_DEBUG(("Updating client entry"));
396 silc_parse_nickname(nickname, &client_entry->nickname,
397 &client_entry->server, &client_entry->num);
400 client_entry->username = strdup(username);
402 id_cache->data = client_entry->nickname;
403 silc_idcache_sort_by_data(conn->client_cache);
405 silc_free(client_id);
408 /* Notify application */
409 COMMAND_REPLY((ARGS, client_entry, nickname, username));
412 /* Received reply for IDENTIFY command. This maybe called several times
413 for one IDENTIFY command as server may reply with list of results.
414 This is totally silent and does not print anything on screen. */
416 SILC_CLIENT_CMD_REPLY_FUNC(identify)
418 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
419 SilcCommandStatus status;
422 SILC_LOG_DEBUG(("Start"));
424 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
425 SILC_GET16_MSB(status, tmp);
426 if (status != SILC_STATUS_OK &&
427 status != SILC_STATUS_LIST_START &&
428 status != SILC_STATUS_LIST_ITEM &&
429 status != SILC_STATUS_LIST_END) {
434 /* Save one IDENTIFY entry */
435 if (status == SILC_STATUS_OK)
436 silc_client_command_reply_identify_save(cmd, status);
439 if (status == SILC_STATUS_LIST_START ||
440 status == SILC_STATUS_LIST_ITEM ||
441 status == SILC_STATUS_LIST_END)
442 silc_client_command_reply_identify_save(cmd, status);
444 /* Pending callbacks are not executed if this was an list entry */
445 if (status != SILC_STATUS_OK &&
446 status != SILC_STATUS_LIST_END) {
447 silc_client_command_reply_free(cmd);
451 /* Execute any pending command callbacks */
452 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
455 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
456 silc_client_command_reply_free(cmd);
459 /* Received reply for command NICK. If everything went without errors
460 we just received our new Client ID. */
462 SILC_CLIENT_CMD_REPLY_FUNC(nick)
464 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
465 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
466 SilcCommandStatus status;
469 unsigned int argc, len;
471 SILC_LOG_DEBUG(("Start"));
473 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
474 if (status != SILC_STATUS_OK) {
475 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
476 silc_client_command_status_message(status));
481 argc = silc_argument_get_arg_num(cmd->args);
482 if (argc < 2 || argc > 2) {
483 cmd->client->ops->say(cmd->client, conn,
484 "Cannot set nickname: bad reply to command");
489 /* Take received Client ID */
490 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
491 idp = silc_id_payload_parse_data(tmp, len);
496 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
498 /* Notify application */
499 COMMAND_REPLY((ARGS, conn->local_entry));
501 /* Execute any pending command callbacks */
502 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
505 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
506 silc_client_command_reply_free(cmd);
509 SILC_CLIENT_CMD_REPLY_FUNC(list)
513 /* Received reply to topic command. */
515 SILC_CLIENT_CMD_REPLY_FUNC(topic)
517 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
518 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
519 SilcCommandStatus status;
520 SilcChannelEntry channel;
521 SilcChannelID *channel_id = NULL;
522 SilcIDCacheEntry id_cache = NULL;
525 unsigned int argc, len;
527 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
528 if (status != SILC_STATUS_OK) {
529 cmd->client->ops->say(cmd->client, conn,
530 "%s", silc_client_command_status_message(status));
532 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
533 silc_client_command_reply_free(cmd);
537 argc = silc_argument_get_arg_num(cmd->args);
538 if (argc < 1 || argc > 3) {
543 /* Take Channel ID */
544 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
549 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
553 channel_id = silc_id_payload_parse_id(tmp, len);
557 /* Get the channel name */
558 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
559 SILC_ID_CHANNEL, &id_cache)) {
560 silc_free(channel_id);
565 channel = (SilcChannelEntry)id_cache->context;
567 cmd->client->ops->say(cmd->client, conn,
568 "Topic on channel %s: %s", channel->channel_name,
571 /* Notify application */
572 COMMAND_REPLY((ARGS, channel, topic));
574 /* Execute any pending command callbacks */
575 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
578 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
579 silc_client_command_reply_free(cmd);
582 /* Received reply to invite command. */
584 SILC_CLIENT_CMD_REPLY_FUNC(invite)
586 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
587 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
588 SilcCommandStatus status;
591 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
592 SILC_GET16_MSB(status, tmp);
593 if (status != SILC_STATUS_OK) {
594 cmd->client->ops->say(cmd->client, conn,
595 "%s", silc_client_command_status_message(status));
597 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
598 silc_client_command_reply_free(cmd);
602 /* Notify application */
603 COMMAND_REPLY((ARGS));
605 /* Execute any pending command callbacks */
606 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
608 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
609 silc_client_command_reply_free(cmd);
612 SILC_CLIENT_CMD_REPLY_FUNC(kill)
616 /* Received reply to INFO command. We receive the server ID and some
617 information about the server user requested. */
619 SILC_CLIENT_CMD_REPLY_FUNC(info)
621 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
622 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
623 SilcClient client = cmd->client;
624 SilcCommandStatus status;
627 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
628 SILC_GET16_MSB(status, tmp);
629 if (status != SILC_STATUS_OK) {
630 cmd->client->ops->say(cmd->client, conn,
631 "%s", silc_client_command_status_message(status));
633 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
634 silc_client_command_reply_free(cmd);
639 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
643 /* XXX save server id */
645 /* Get server info */
646 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
650 client->ops->say(cmd->client, conn, "Info: %s", tmp);
652 /* Notify application */
653 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
655 /* Execute any pending command callbacks */
656 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
659 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
660 silc_client_command_reply_free(cmd);
663 /* Received reply to PING command. The reply time is shown to user. */
665 SILC_CLIENT_CMD_REPLY_FUNC(ping)
667 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
668 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
669 SilcCommandStatus status;
672 time_t diff, curtime;
674 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
675 if (status != SILC_STATUS_OK) {
676 cmd->client->ops->say(cmd->client, conn,
677 "%s", silc_client_command_status_message(status));
682 curtime = time(NULL);
683 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
684 cmd->packet->src_id_type);
690 for (i = 0; i < conn->ping_count; i++) {
691 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
692 diff = curtime - conn->ping[i].start_time;
693 cmd->client->ops->say(cmd->client, conn,
694 "Ping reply from %s: %d second%s",
695 conn->ping[i].dest_name, diff,
696 diff == 1 ? "" : "s");
698 conn->ping[i].start_time = 0;
699 silc_free(conn->ping[i].dest_id);
700 conn->ping[i].dest_id = NULL;
701 silc_free(conn->ping[i].dest_name);
702 conn->ping[i].dest_name = NULL;
709 /* Notify application */
710 COMMAND_REPLY((ARGS));
712 /* Execute any pending command callbacks */
713 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
716 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
717 silc_client_command_reply_free(cmd);
720 /* Received reply for JOIN command. */
722 SILC_CLIENT_CMD_REPLY_FUNC(join)
724 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
725 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
726 SilcCommandStatus status;
727 SilcIDPayload idp = NULL;
728 SilcChannelEntry channel;
729 SilcIDCacheEntry id_cache = NULL;
731 unsigned int argc, mode, len, list_count;
732 char *topic, *tmp, *channel_name = NULL, *hmac;
733 SilcBuffer keyp, client_id_list, client_mode_list;
736 SILC_LOG_DEBUG(("Start"));
738 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
739 if (status != SILC_STATUS_OK) {
740 cmd->client->ops->say(cmd->client, conn,
741 "%s", silc_client_command_status_message(status));
746 argc = silc_argument_get_arg_num(cmd->args);
747 if (argc < 7 || argc > 14) {
748 cmd->client->ops->say(cmd->client, conn,
749 "Cannot join channel: Bad reply packet");
754 /* Get channel name */
755 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
757 cmd->client->ops->say(cmd->client, conn,
758 "Cannot join channel: Bad reply packet");
762 channel_name = strdup(tmp);
765 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
767 cmd->client->ops->say(cmd->client, conn,
768 "Cannot join channel: Bad reply packet");
770 silc_free(channel_name);
773 idp = silc_id_payload_parse_data(tmp, len);
776 silc_free(channel_name);
780 /* Get channel mode */
781 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
783 SILC_GET32_MSB(mode, tmp);
787 /* Get channel key */
788 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
790 silc_id_payload_free(idp);
791 silc_free(channel_name);
794 keyp = silc_buffer_alloc(len);
795 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
796 silc_buffer_put(keyp, tmp, len);
799 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
801 /* Save received Channel ID. This actually creates the channel */
802 channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
804 silc_id_payload_free(idp);
807 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
809 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
810 cmd->client->ops->say(cmd->client, conn,
811 "Cannot join channel: Unsupported HMAC `%s'",
814 silc_free(channel_name);
819 /* Get the list count */
820 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
823 SILC_GET32_MSB(list_count, tmp);
825 /* Get Client ID list */
826 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
830 client_id_list = silc_buffer_alloc(len);
831 silc_buffer_pull_tail(client_id_list, len);
832 silc_buffer_put(client_id_list, tmp, len);
834 /* Get client mode list */
835 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
839 client_mode_list = silc_buffer_alloc(len);
840 silc_buffer_pull_tail(client_mode_list, len);
841 silc_buffer_put(client_mode_list, tmp, len);
843 /* Add clients we received in the reply to the channel */
844 for (i = 0; i < list_count; i++) {
845 unsigned short idp_len;
847 SilcClientID *client_id;
848 SilcClientEntry client_entry;
851 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
853 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
858 SILC_GET32_MSB(mode, client_mode_list->data);
860 /* Check if we have this client cached already. */
861 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
862 SILC_ID_CLIENT, &id_cache)) {
863 /* No, we don't have it, add entry for it. */
864 client_entry = silc_calloc(1, sizeof(*client_entry));
865 client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
866 silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT,
867 client_entry->id, (void *)client_entry, FALSE, FALSE);
869 /* Yes, we have it already */
870 client_entry = (SilcClientEntry)id_cache->context;
873 /* Join the client to the channel */
874 chu = silc_calloc(1, sizeof(*chu));
875 chu->client = client_entry;
877 silc_list_add(channel->clients, chu);
878 silc_free(client_id);
880 silc_buffer_pull(client_id_list, idp_len);
881 silc_buffer_pull(client_mode_list, 4);
883 silc_buffer_push(client_id_list, client_id_list->data -
884 client_id_list->head);
885 silc_buffer_push(client_mode_list, client_mode_list->data -
886 client_mode_list->head);
888 /* Save channel key */
889 silc_client_save_channel_key(conn, keyp, channel);
891 /* Notify application */
892 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
893 NULL, topic, hmac, list_count, client_id_list,
896 /* Execute any pending command callbacks */
897 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
899 silc_buffer_free(keyp);
900 silc_buffer_free(client_id_list);
901 silc_buffer_free(client_mode_list);
904 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
905 silc_client_command_reply_free(cmd);
908 /* Received reply for MOTD command */
910 SILC_CLIENT_CMD_REPLY_FUNC(motd)
912 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
913 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
914 SilcCommandStatus status;
915 unsigned int argc, i;
917 char *motd = NULL, *cp, line[256];
919 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
920 SILC_GET16_MSB(status, tmp);
921 if (status != SILC_STATUS_OK) {
922 cmd->client->ops->say(cmd->client, conn,
923 "%s", silc_client_command_status_message(status));
928 argc = silc_argument_get_arg_num(cmd->args);
935 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
944 if (cp[i++] == '\n') {
945 memset(line, 0, sizeof(line));
946 strncat(line, cp, i - 1);
952 cmd->client->ops->say(cmd->client, conn, "%s", line);
961 /* Notify application */
962 COMMAND_REPLY((ARGS, motd));
964 /* Execute any pending command callbacks */
965 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
968 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
969 silc_client_command_reply_free(cmd);
972 SILC_CLIENT_CMD_REPLY_FUNC(umode)
976 /* Received reply for CMODE command. */
978 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
980 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
981 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
982 SilcCommandStatus status;
985 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
986 if (status != SILC_STATUS_OK) {
987 cmd->client->ops->say(cmd->client, conn,
988 "%s", silc_client_command_status_message(status));
993 /* Get channel mode */
994 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1000 /* Notify application */
1001 COMMAND_REPLY((ARGS, tmp));
1003 /* Execute any pending command callbacks */
1004 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1007 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1008 silc_client_command_reply_free(cmd);
1011 /* Received reply for CUMODE command */
1013 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1015 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1016 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1017 SilcCommandStatus status;
1018 SilcIDCacheEntry id_cache = NULL;
1019 SilcClientID *client_id;
1020 unsigned char *tmp, *id;
1023 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1024 if (status != SILC_STATUS_OK) {
1025 cmd->client->ops->say(cmd->client, conn,
1026 "%s", silc_client_command_status_message(status));
1027 COMMAND_REPLY_ERROR;
1031 /* Get channel mode */
1032 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1034 COMMAND_REPLY_ERROR;
1039 id = silc_argument_get_arg_type(cmd->args, 3, &len);
1041 COMMAND_REPLY_ERROR;
1044 client_id = silc_id_payload_parse_id(id, len);
1046 COMMAND_REPLY_ERROR;
1050 /* Get client entry */
1051 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1052 SILC_ID_CLIENT, &id_cache)) {
1053 COMMAND_REPLY_ERROR;
1057 /* Notify application */
1058 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1059 silc_free(client_id);
1061 /* Execute any pending command callbacks */
1062 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1065 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1066 silc_client_command_reply_free(cmd);
1069 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1071 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1072 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1073 SilcCommandStatus status;
1076 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1077 SILC_GET16_MSB(status, tmp);
1078 if (status != SILC_STATUS_OK) {
1079 cmd->client->ops->say(cmd->client, conn,
1080 "%s", silc_client_command_status_message(status));
1081 COMMAND_REPLY_ERROR;
1085 /* Notify application */
1086 COMMAND_REPLY((ARGS));
1088 /* Execute any pending command callbacks */
1089 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1092 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1093 silc_client_command_reply_free(cmd);
1096 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1100 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1104 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1106 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1107 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1108 SilcCommandStatus status;
1111 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1112 SILC_GET16_MSB(status, tmp);
1113 if (status != SILC_STATUS_OK) {
1114 cmd->client->ops->say(cmd->client, conn,
1115 "%s", silc_client_command_status_message(status));
1116 COMMAND_REPLY_ERROR;
1120 /* Notify application */
1121 COMMAND_REPLY((ARGS));
1123 /* Execute any pending command callbacks */
1124 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1127 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1128 silc_client_command_reply_free(cmd);
1131 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1133 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1134 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1135 SilcCommandStatus status;
1138 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1139 SILC_GET16_MSB(status, tmp);
1140 if (status != SILC_STATUS_OK) {
1141 cmd->client->ops->say(cmd->client, conn,
1142 "%s", silc_client_command_status_message(status));
1143 COMMAND_REPLY_ERROR;
1147 /* Notify application */
1148 COMMAND_REPLY((ARGS));
1150 /* Execute any pending command callbacks */
1151 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1154 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1155 silc_client_command_reply_free(cmd);
1158 SILC_CLIENT_CMD_REPLY_FUNC(close)
1160 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1161 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1162 SilcCommandStatus status;
1165 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1166 SILC_GET16_MSB(status, tmp);
1167 if (status != SILC_STATUS_OK) {
1168 cmd->client->ops->say(cmd->client, conn,
1169 "%s", silc_client_command_status_message(status));
1170 COMMAND_REPLY_ERROR;
1174 /* Notify application */
1175 COMMAND_REPLY((ARGS));
1177 /* Execute any pending command callbacks */
1178 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1181 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1182 silc_client_command_reply_free(cmd);
1185 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1187 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1188 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1189 SilcCommandStatus status;
1192 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1193 SILC_GET16_MSB(status, tmp);
1194 if (status != SILC_STATUS_OK) {
1195 cmd->client->ops->say(cmd->client, conn,
1196 "%s", silc_client_command_status_message(status));
1197 COMMAND_REPLY_ERROR;
1201 /* Notify application */
1202 COMMAND_REPLY((ARGS));
1204 /* Execute any pending command callbacks */
1205 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1208 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1209 silc_client_command_reply_free(cmd);
1212 /* Reply to LEAVE command. */
1214 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1216 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1217 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1218 SilcCommandStatus status;
1221 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1222 SILC_GET16_MSB(status, tmp);
1223 if (status != SILC_STATUS_OK) {
1224 cmd->client->ops->say(cmd->client, conn,
1225 "%s", silc_client_command_status_message(status));
1226 COMMAND_REPLY_ERROR;
1230 /* Notify application */
1231 COMMAND_REPLY((ARGS));
1233 /* Execute any pending command callbacks */
1234 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1237 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1238 silc_client_command_reply_free(cmd);
1241 /* Reply to USERS command. Received list of client ID's and theirs modes
1242 on the channel we requested. */
1244 SILC_CLIENT_CMD_REPLY_FUNC(users)
1246 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1247 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1248 SilcCommandStatus status;
1249 SilcIDCacheEntry id_cache = NULL;
1250 SilcChannelEntry channel;
1251 SilcChannelUser chu;
1252 SilcChannelID *channel_id = NULL;
1253 SilcBuffer client_id_list;
1254 SilcBuffer client_mode_list;
1256 unsigned int tmp_len, list_count;
1258 unsigned char **res_argv = NULL;
1259 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1261 SILC_LOG_DEBUG(("Start"));
1263 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1264 SILC_GET16_MSB(status, tmp);
1265 if (status != SILC_STATUS_OK) {
1266 cmd->client->ops->say(cmd->client, conn,
1267 "%s", silc_client_command_status_message(status));
1268 COMMAND_REPLY_ERROR;
1272 /* Get channel ID */
1273 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1276 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1280 /* Get the list count */
1281 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1284 SILC_GET32_MSB(list_count, tmp);
1286 /* Get Client ID list */
1287 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1291 client_id_list = silc_buffer_alloc(tmp_len);
1292 silc_buffer_pull_tail(client_id_list, tmp_len);
1293 silc_buffer_put(client_id_list, tmp, tmp_len);
1295 /* Get client mode list */
1296 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1300 client_mode_list = silc_buffer_alloc(tmp_len);
1301 silc_buffer_pull_tail(client_mode_list, tmp_len);
1302 silc_buffer_put(client_mode_list, tmp, tmp_len);
1304 /* Get channel entry */
1305 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1306 SILC_ID_CHANNEL, &id_cache)) {
1307 COMMAND_REPLY_ERROR;
1310 channel = (SilcChannelEntry)id_cache->context;
1312 /* Remove old client list from channel. */
1313 silc_list_start(channel->clients);
1314 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1315 silc_list_del(channel->clients, chu);
1319 /* Cache the received Client ID's and modes. This cache expires
1320 whenever server sends notify message to channel. It means two things;
1321 some user has joined or leaved the channel. XXX! */
1322 for (i = 0; i < list_count; i++) {
1323 unsigned short idp_len;
1325 SilcClientID *client_id;
1326 SilcClientEntry client;
1329 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1331 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1336 SILC_GET32_MSB(mode, client_mode_list->data);
1338 /* Check if we have this client cached already. */
1339 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1340 SILC_ID_CLIENT, &id_cache)) {
1341 /* No we don't have it, query it from the server. Assemble argument
1342 table that will be sent fr the IDENTIFY command later. */
1343 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1345 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1347 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1349 res_argv[res_argc] = client_id_list->data;
1350 res_argv_lens[res_argc] = idp_len;
1351 res_argv_types[res_argc] = res_argc + 3;
1354 /* Found the client, join it to the channel */
1355 client = (SilcClientEntry)id_cache->context;
1356 chu = silc_calloc(1, sizeof(*chu));
1357 chu->client = client;
1359 silc_list_add(channel->clients, chu);
1361 silc_free(client_id);
1365 silc_buffer_pull(client_id_list, idp_len);
1366 silc_buffer_pull(client_mode_list, 4);
1369 /* Query the client information from server if the list included clients
1370 that we don't know about. */
1374 /* Send the IDENTIFY command to server */
1375 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1376 res_argc, res_argv, res_argv_lens,
1377 res_argv_types, ++conn->cmd_ident);
1378 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1379 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1382 /* Register pending command callback. After we've received the IDENTIFY
1383 command reply we will reprocess this command reply by re-calling this
1384 USERS command reply callback. */
1385 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1386 NULL, silc_client_command_reply_users, cmd);
1388 silc_buffer_free(res_cmd);
1390 silc_free(channel_id);
1392 silc_free(res_argv);
1393 silc_free(res_argv_lens);
1394 silc_free(res_argv_types);
1398 /* Notify application */
1399 COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1401 /* Execute any pending command callbacks */
1402 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1404 silc_buffer_free(client_id_list);
1405 silc_buffer_free(client_mode_list);
1409 silc_free(channel_id);
1410 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1411 silc_client_command_reply_free(cmd);