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 /* Status message structure. Messages are defined below. */
72 SilcCommandStatus status;
74 } SilcCommandStatusMessage;
76 /* Status messages returned by the server */
77 #define STAT(x) SILC_STATUS_ERR_##x
78 const SilcCommandStatusMessage silc_command_status_messages[] = {
80 { STAT(NO_SUCH_NICK), "No such nickname" },
81 { STAT(NO_SUCH_CHANNEL), "No such channel" },
82 { STAT(NO_SUCH_SERVER), "No such server" },
83 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
84 { STAT(NO_RECIPIENT), "No recipient given" },
85 { STAT(UNKNOWN_COMMAND), "Unknown command" },
86 { STAT(WILDCARDS), "Unknown command" },
87 { STAT(NO_CLIENT_ID), "No Client ID given" },
88 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
89 { STAT(NO_SERVER_ID), "No Server ID given" },
90 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
91 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
92 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
93 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
94 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
95 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
96 { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
97 { STAT(USER_ON_CHANNEL), "User already on the channel" },
98 { STAT(NOT_REGISTERED), "You have not registered" },
99 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
100 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
101 { STAT(PERM_DENIED), "Your host is not among the privileged" },
102 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
103 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
104 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
105 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
106 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
107 { STAT(UNKNOWN_MODE), "Unknown mode" },
108 { STAT(NOT_YOU), "Cannot change mode for other users" },
109 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
110 { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
111 { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" },
112 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
113 { STAT(BAD_NICKNAME), "Bad nickname" },
114 { STAT(BAD_CHANNEL), "Bad channel name" },
115 { STAT(AUTH_FAILED), "Authentication failed" },
116 { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
121 /* Command reply operation that is called at the end of all command replys.
122 Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
123 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
124 #define ARGS cmd->client, cmd->sock->user_data, \
125 cmd->payload, TRUE, silc_command_get(cmd->payload), status
127 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
128 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
129 cmd->sock->user_data, cmd->payload, FALSE, \
130 silc_command_get(cmd->payload), status)
132 /* Process received command reply. */
134 void silc_client_command_reply_process(SilcClient client,
135 SilcSocketConnection sock,
136 SilcPacketContext *packet)
138 SilcBuffer buffer = packet->buffer;
139 SilcClientCommandReply *cmd;
140 SilcClientCommandReplyContext ctx;
141 SilcCommandPayload payload;
143 unsigned short ident;
145 /* Get command reply payload from packet */
146 payload = silc_command_payload_parse(buffer);
148 /* Silently ignore bad reply packet */
149 SILC_LOG_DEBUG(("Bad command reply packet"));
153 /* Allocate command reply context. This must be free'd by the
154 command reply routine receiving it. */
155 ctx = silc_calloc(1, sizeof(*ctx));
156 ctx->client = client;
158 ctx->payload = payload;
159 ctx->args = silc_command_get_args(ctx->payload);
160 ctx->packet = packet;
161 ident = silc_command_get_ident(ctx->payload);
163 /* Check for pending commands and mark to be exeucted */
164 silc_client_command_pending_check(sock->user_data, ctx,
165 silc_command_get(ctx->payload), ident);
167 /* Execute command reply */
168 command = silc_command_get(ctx->payload);
169 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
170 if (cmd->cmd == command)
173 if (cmd == NULL || !cmd->cb) {
181 /* Returns status message string */
184 silc_client_command_status_message(SilcCommandStatus status)
188 for (i = 0; silc_command_status_messages[i].message; i++) {
189 if (silc_command_status_messages[i].status == status)
193 if (silc_command_status_messages[i].message == NULL)
196 return silc_command_status_messages[i].message;
199 /* Free command reply context and its internals. */
201 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
204 silc_command_free_payload(cmd->payload);
210 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
211 SilcCommandStatus status)
215 unsigned char *id_data;
216 char *nickname = NULL, *username = NULL;
217 char *realname = NULL;
218 SilcClientID *client_id;
219 SilcIDCacheEntry id_cache = NULL;
220 SilcClientEntry client_entry = NULL;
221 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
223 memset(buf, 0, sizeof(buf));
225 argc = silc_argument_get_arg_num(cmd->args);
227 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
233 client_id = silc_id_payload_parse_id(id_data, len);
239 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
241 strncat(buf, nickname, len);
242 strncat(buf, " is ", 4);
245 username = silc_argument_get_arg_type(cmd->args, 4, &len);
247 strncat(buf, username, len);
250 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
252 strncat(buf, " (", 2);
253 strncat(buf, realname, len);
254 strncat(buf, ")", 1);
257 /* Check if we have this client cached already. */
258 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
259 SILC_ID_CLIENT, &id_cache)) {
260 client_entry = silc_calloc(1, sizeof(*client_entry));
261 client_entry->id = client_id;
262 silc_parse_nickname(nickname, &client_entry->nickname,
263 &client_entry->server, &client_entry->num);
264 client_entry->username = strdup(username);
266 client_entry->realname = strdup(realname);
268 /* Add client to cache */
269 silc_idcache_add(conn->client_cache, client_entry->nickname,
270 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
272 client_entry = (SilcClientEntry)id_cache->context;
273 if (client_entry->nickname)
274 silc_free(client_entry->nickname);
275 if (client_entry->server)
276 silc_free(client_entry->server);
277 if (client_entry->username)
278 silc_free(client_entry->username);
279 if (client_entry->realname)
280 silc_free(client_entry->realname);
282 silc_parse_nickname(nickname, &client_entry->nickname,
283 &client_entry->server, &client_entry->num);
284 client_entry->username = strdup(username);
286 client_entry->realname = strdup(realname);
288 id_cache->data = client_entry->nickname;
289 silc_idcache_sort_by_data(conn->client_cache);
291 silc_free(client_id);
295 cmd->client->ops->say(cmd->client, conn, "%s", buf);
297 /* Notify application */
298 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
302 /* Received reply for WHOIS command. This maybe called several times
303 for one WHOIS command as server may reply with list of results. */
305 SILC_CLIENT_CMD_REPLY_FUNC(whois)
307 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
308 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
309 SilcCommandStatus status;
312 SILC_LOG_DEBUG(("Start"));
314 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
315 SILC_GET16_MSB(status, tmp);
316 if (status != SILC_STATUS_OK &&
317 status != SILC_STATUS_LIST_START &&
318 status != SILC_STATUS_LIST_ITEM &&
319 status != SILC_STATUS_LIST_END) {
320 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
321 /* Take nickname which may be provided */
322 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
324 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
325 silc_client_command_status_message(status));
327 cmd->client->ops->say(cmd->client, conn, "%s",
328 silc_client_command_status_message(status));
332 cmd->client->ops->say(cmd->client, conn,
333 "%s", silc_client_command_status_message(status));
339 /* Display one whois reply */
340 if (status == SILC_STATUS_OK) {
341 silc_client_command_reply_whois_print(cmd, status);
344 /* XXX list should not be displayed untill all items has been received. */
345 if (status == SILC_STATUS_LIST_START) {
346 silc_client_command_reply_whois_print(cmd, status);
349 if (status == SILC_STATUS_LIST_ITEM) {
350 silc_client_command_reply_whois_print(cmd, status);
353 if (status == SILC_STATUS_LIST_END) {
354 silc_client_command_reply_whois_print(cmd, status);
357 /* Execute any pending command callbacks */
358 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
361 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
362 silc_client_command_reply_free(cmd);
365 /* Received reply for WHOWAS command. */
367 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
372 /* Received reply for IDENTIFY command. This maybe called several times
373 for one IDENTIFY command as server may reply with list of results.
374 This is totally silent and does not print anything on screen. */
376 SILC_CLIENT_CMD_REPLY_FUNC(identify)
378 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
379 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
380 SilcClientEntry client_entry;
381 SilcIDCacheEntry id_cache = NULL;
382 SilcCommandStatus status;
385 SILC_LOG_DEBUG(("Start"));
387 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
388 SILC_GET16_MSB(status, tmp);
389 if (status != SILC_STATUS_OK &&
390 status != SILC_STATUS_LIST_START &&
391 status != SILC_STATUS_LIST_ITEM &&
392 status != SILC_STATUS_LIST_END) {
393 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
394 /* Take nickname which may be provided */
395 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
397 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
398 silc_client_command_status_message(status));
400 cmd->client->ops->say(cmd->client, conn, "%s",
401 silc_client_command_status_message(status));
405 cmd->client->ops->say(cmd->client, conn,
406 "%s", silc_client_command_status_message(status));
412 /* Display one whois reply */
413 if (status == SILC_STATUS_OK ||
414 status == SILC_STATUS_LIST_START ||
415 status == SILC_STATUS_LIST_ITEM ||
416 status == SILC_STATUS_LIST_END) {
418 unsigned char *id_data;
421 SilcClientID *client_id;
423 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
426 client_id = silc_id_payload_parse_id(id_data, len);
430 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
431 username = silc_argument_get_arg_type(cmd->args, 4, NULL);
433 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
434 SILC_ID_CLIENT, &id_cache)) {
435 client_entry = silc_calloc(1, sizeof(*client_entry));
436 client_entry->id = client_id;
437 silc_parse_nickname(nickname, &client_entry->nickname,
438 &client_entry->server, &client_entry->num);
440 client_entry->username = strdup(username);
442 /* Add client to cache */
443 silc_idcache_add(conn->client_cache, client_entry->nickname,
444 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
446 client_entry = (SilcClientEntry)id_cache->context;
447 if (client_entry->nickname)
448 silc_free(client_entry->nickname);
449 if (client_entry->server)
450 silc_free(client_entry->server);
451 if (username && client_entry->username)
452 silc_free(client_entry->username);
454 silc_parse_nickname(nickname, &client_entry->nickname,
455 &client_entry->server, &client_entry->num);
458 client_entry->username = strdup(username);
460 id_cache->data = client_entry->nickname;
461 silc_idcache_sort_by_data(conn->client_cache);
463 silc_free(client_id);
467 if (status != SILC_STATUS_OK &&
468 status != SILC_STATUS_LIST_END) {
469 silc_client_command_reply_free(cmd);
473 /* Execute any pending command callbacks */
474 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
477 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
478 silc_client_command_reply_free(cmd);
481 /* Received reply for command NICK. If everything went without errors
482 we just received our new Client ID. */
484 SILC_CLIENT_CMD_REPLY_FUNC(nick)
486 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
487 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
488 SilcCommandStatus status;
491 unsigned int argc, len;
493 SILC_LOG_DEBUG(("Start"));
495 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
496 if (status != SILC_STATUS_OK) {
497 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
498 silc_client_command_status_message(status));
503 argc = silc_argument_get_arg_num(cmd->args);
504 if (argc < 2 || argc > 2) {
505 cmd->client->ops->say(cmd->client, conn,
506 "Cannot set nickname: bad reply to command");
511 /* Take received Client ID */
512 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
513 idp = silc_id_payload_parse_data(tmp, len);
518 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
520 /* Notify application */
521 COMMAND_REPLY((ARGS, conn->local_entry));
523 /* Execute any pending command callbacks */
524 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
527 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
528 silc_client_command_reply_free(cmd);
531 SILC_CLIENT_CMD_REPLY_FUNC(list)
535 /* Received reply to topic command. */
537 SILC_CLIENT_CMD_REPLY_FUNC(topic)
539 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
540 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
541 SilcCommandStatus status;
542 SilcChannelEntry channel;
543 SilcChannelID *channel_id = NULL;
544 SilcIDCacheEntry id_cache = NULL;
547 unsigned int argc, len;
549 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
550 if (status != SILC_STATUS_OK) {
551 cmd->client->ops->say(cmd->client, conn,
552 "%s", silc_client_command_status_message(status));
554 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
555 silc_client_command_reply_free(cmd);
559 argc = silc_argument_get_arg_num(cmd->args);
560 if (argc < 1 || argc > 3) {
565 /* Take Channel ID */
566 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
571 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
575 channel_id = silc_id_payload_parse_id(tmp, len);
579 /* Get the channel name */
580 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
581 SILC_ID_CHANNEL, &id_cache)) {
582 silc_free(channel_id);
587 channel = (SilcChannelEntry)id_cache->context;
589 cmd->client->ops->say(cmd->client, conn,
590 "Topic on channel %s: %s", channel->channel_name,
593 /* Notify application */
594 COMMAND_REPLY((ARGS, channel, topic));
596 /* Execute any pending command callbacks */
597 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
600 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
601 silc_client_command_reply_free(cmd);
604 /* Received reply to invite command. */
606 SILC_CLIENT_CMD_REPLY_FUNC(invite)
608 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
609 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
610 SilcCommandStatus status;
613 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
614 SILC_GET16_MSB(status, tmp);
615 if (status != SILC_STATUS_OK) {
616 cmd->client->ops->say(cmd->client, conn,
617 "%s", silc_client_command_status_message(status));
619 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
620 silc_client_command_reply_free(cmd);
624 /* Notify application */
625 COMMAND_REPLY((ARGS));
627 /* Execute any pending command callbacks */
628 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
630 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
631 silc_client_command_reply_free(cmd);
634 SILC_CLIENT_CMD_REPLY_FUNC(kill)
638 /* Received reply to INFO command. We receive the server ID and some
639 information about the server user requested. */
641 SILC_CLIENT_CMD_REPLY_FUNC(info)
643 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
644 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
645 SilcClient client = cmd->client;
646 SilcCommandStatus status;
649 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
650 SILC_GET16_MSB(status, tmp);
651 if (status != SILC_STATUS_OK) {
652 cmd->client->ops->say(cmd->client, conn,
653 "%s", silc_client_command_status_message(status));
655 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
656 silc_client_command_reply_free(cmd);
661 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
665 /* XXX save server id */
667 /* Get server info */
668 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
672 client->ops->say(cmd->client, conn, "Info: %s", tmp);
674 /* Notify application */
675 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
677 /* Execute any pending command callbacks */
678 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
681 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
682 silc_client_command_reply_free(cmd);
685 /* Received reply to PING command. The reply time is shown to user. */
687 SILC_CLIENT_CMD_REPLY_FUNC(ping)
689 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
690 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
691 SilcCommandStatus status;
694 time_t diff, curtime;
696 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
697 if (status != SILC_STATUS_OK) {
698 cmd->client->ops->say(cmd->client, conn,
699 "%s", silc_client_command_status_message(status));
704 curtime = time(NULL);
705 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
706 cmd->packet->src_id_type);
712 for (i = 0; i < conn->ping_count; i++) {
713 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
714 diff = curtime - conn->ping[i].start_time;
715 cmd->client->ops->say(cmd->client, conn,
716 "Ping reply from %s: %d second%s",
717 conn->ping[i].dest_name, diff,
718 diff == 1 ? "" : "s");
720 conn->ping[i].start_time = 0;
721 silc_free(conn->ping[i].dest_id);
722 conn->ping[i].dest_id = NULL;
723 silc_free(conn->ping[i].dest_name);
724 conn->ping[i].dest_name = NULL;
731 /* Notify application */
732 COMMAND_REPLY((ARGS));
734 /* Execute any pending command callbacks */
735 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
738 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
739 silc_client_command_reply_free(cmd);
742 /* Received reply for JOIN command. */
744 SILC_CLIENT_CMD_REPLY_FUNC(join)
746 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
747 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
748 SilcCommandStatus status;
749 SilcIDPayload idp = NULL;
750 SilcChannelEntry channel;
751 SilcIDCacheEntry id_cache = NULL;
753 unsigned int argc, mode, len, list_count;
754 char *topic, *tmp, *channel_name = NULL, *hmac;
755 SilcBuffer keyp, client_id_list, client_mode_list;
758 SILC_LOG_DEBUG(("Start"));
760 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
761 if (status != SILC_STATUS_OK) {
762 cmd->client->ops->say(cmd->client, conn,
763 "%s", silc_client_command_status_message(status));
768 argc = silc_argument_get_arg_num(cmd->args);
769 if (argc < 7 || argc > 14) {
770 cmd->client->ops->say(cmd->client, conn,
771 "Cannot join channel: Bad reply packet");
776 /* Get channel name */
777 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
779 cmd->client->ops->say(cmd->client, conn,
780 "Cannot join channel: Bad reply packet");
784 channel_name = strdup(tmp);
787 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
789 cmd->client->ops->say(cmd->client, conn,
790 "Cannot join channel: Bad reply packet");
792 silc_free(channel_name);
795 idp = silc_id_payload_parse_data(tmp, len);
798 silc_free(channel_name);
802 /* Get channel mode */
803 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
805 SILC_GET32_MSB(mode, tmp);
809 /* Get channel key */
810 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
812 silc_id_payload_free(idp);
813 silc_free(channel_name);
816 keyp = silc_buffer_alloc(len);
817 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
818 silc_buffer_put(keyp, tmp, len);
821 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
823 /* Save received Channel ID. This actually creates the channel */
824 channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
826 silc_id_payload_free(idp);
829 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
831 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
832 cmd->client->ops->say(cmd->client, conn,
833 "Cannot join channel: Unsupported HMAC `%s'",
836 silc_free(channel_name);
841 /* Get the list count */
842 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
845 SILC_GET32_MSB(list_count, tmp);
847 /* Get Client ID list */
848 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
852 client_id_list = silc_buffer_alloc(len);
853 silc_buffer_pull_tail(client_id_list, len);
854 silc_buffer_put(client_id_list, tmp, len);
856 /* Get client mode list */
857 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
861 client_mode_list = silc_buffer_alloc(len);
862 silc_buffer_pull_tail(client_mode_list, len);
863 silc_buffer_put(client_mode_list, tmp, len);
865 /* Add clients we received in the reply to the channel */
866 for (i = 0; i < list_count; i++) {
867 unsigned short idp_len;
869 SilcClientID *client_id;
870 SilcClientEntry client_entry;
873 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
875 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
880 SILC_GET32_MSB(mode, client_mode_list->data);
882 /* Check if we have this client cached already. */
883 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
884 SILC_ID_CLIENT, &id_cache)) {
885 /* No, we don't have it, add entry for it. */
886 client_entry = silc_calloc(1, sizeof(*client_entry));
887 client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
888 silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT,
889 client_entry->id, (void *)client_entry, FALSE);
891 /* Yes, we have it already */
892 client_entry = (SilcClientEntry)id_cache->context;
895 /* Join the client to the channel */
896 chu = silc_calloc(1, sizeof(*chu));
897 chu->client = client_entry;
899 silc_list_add(channel->clients, chu);
900 silc_free(client_id);
902 silc_buffer_pull(client_id_list, idp_len);
903 silc_buffer_pull(client_mode_list, 4);
905 silc_buffer_push(client_id_list, client_id_list->data -
906 client_id_list->head);
907 silc_buffer_push(client_mode_list, client_mode_list->data -
908 client_mode_list->head);
910 /* Save channel key */
911 silc_client_save_channel_key(conn, keyp, channel);
913 /* Notify application */
914 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
915 NULL, topic, hmac, list_count, client_id_list,
918 /* Execute any pending command callbacks */
919 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
921 silc_buffer_free(keyp);
922 silc_buffer_free(client_id_list);
923 silc_buffer_free(client_mode_list);
926 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
927 silc_client_command_reply_free(cmd);
930 /* Received reply for MOTD command */
932 SILC_CLIENT_CMD_REPLY_FUNC(motd)
934 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
935 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
936 SilcCommandStatus status;
937 unsigned int argc, i;
939 char *motd = NULL, *cp, line[256];
941 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
942 SILC_GET16_MSB(status, tmp);
943 if (status != SILC_STATUS_OK) {
944 cmd->client->ops->say(cmd->client, conn,
945 "%s", silc_client_command_status_message(status));
950 argc = silc_argument_get_arg_num(cmd->args);
957 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
966 if (cp[i++] == '\n') {
967 memset(line, 0, sizeof(line));
968 strncat(line, cp, i - 1);
974 cmd->client->ops->say(cmd->client, conn, "%s", line);
983 /* Notify application */
984 COMMAND_REPLY((ARGS, motd));
986 /* Execute any pending command callbacks */
987 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
990 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
991 silc_client_command_reply_free(cmd);
994 SILC_CLIENT_CMD_REPLY_FUNC(umode)
998 /* Received reply for CMODE command. */
1000 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1002 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1003 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1004 SilcCommandStatus status;
1007 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1008 if (status != SILC_STATUS_OK) {
1009 cmd->client->ops->say(cmd->client, conn,
1010 "%s", silc_client_command_status_message(status));
1011 COMMAND_REPLY_ERROR;
1015 /* Get channel mode */
1016 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1018 COMMAND_REPLY_ERROR;
1022 /* Notify application */
1023 COMMAND_REPLY((ARGS, tmp));
1025 /* Execute any pending command callbacks */
1026 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1029 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1030 silc_client_command_reply_free(cmd);
1033 /* Received reply for CUMODE command */
1035 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1037 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1038 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1039 SilcCommandStatus status;
1040 SilcIDCacheEntry id_cache = NULL;
1041 SilcClientID *client_id;
1042 unsigned char *tmp, *id;
1045 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1046 if (status != SILC_STATUS_OK) {
1047 cmd->client->ops->say(cmd->client, conn,
1048 "%s", silc_client_command_status_message(status));
1049 COMMAND_REPLY_ERROR;
1053 /* Get channel mode */
1054 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1056 COMMAND_REPLY_ERROR;
1061 id = silc_argument_get_arg_type(cmd->args, 3, &len);
1063 COMMAND_REPLY_ERROR;
1066 client_id = silc_id_payload_parse_id(id, len);
1068 COMMAND_REPLY_ERROR;
1072 /* Get client entry */
1073 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1074 SILC_ID_CLIENT, &id_cache)) {
1075 COMMAND_REPLY_ERROR;
1079 /* Notify application */
1080 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1081 silc_free(client_id);
1083 /* Execute any pending command callbacks */
1084 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1087 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1088 silc_client_command_reply_free(cmd);
1091 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1093 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1094 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1095 SilcCommandStatus status;
1098 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1099 SILC_GET16_MSB(status, tmp);
1100 if (status != SILC_STATUS_OK) {
1101 cmd->client->ops->say(cmd->client, conn,
1102 "%s", silc_client_command_status_message(status));
1103 COMMAND_REPLY_ERROR;
1107 /* Notify application */
1108 COMMAND_REPLY((ARGS));
1110 /* Execute any pending command callbacks */
1111 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1114 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1115 silc_client_command_reply_free(cmd);
1118 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1122 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1126 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1128 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1129 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1130 SilcCommandStatus status;
1133 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1134 SILC_GET16_MSB(status, tmp);
1135 if (status != SILC_STATUS_OK) {
1136 cmd->client->ops->say(cmd->client, conn,
1137 "%s", silc_client_command_status_message(status));
1138 COMMAND_REPLY_ERROR;
1142 /* Notify application */
1143 COMMAND_REPLY((ARGS));
1145 /* Execute any pending command callbacks */
1146 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1149 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1150 silc_client_command_reply_free(cmd);
1153 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1155 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1156 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1157 SilcCommandStatus status;
1160 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1161 SILC_GET16_MSB(status, tmp);
1162 if (status != SILC_STATUS_OK) {
1163 cmd->client->ops->say(cmd->client, conn,
1164 "%s", silc_client_command_status_message(status));
1165 COMMAND_REPLY_ERROR;
1169 /* Notify application */
1170 COMMAND_REPLY((ARGS));
1172 /* Execute any pending command callbacks */
1173 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1176 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1177 silc_client_command_reply_free(cmd);
1180 SILC_CLIENT_CMD_REPLY_FUNC(close)
1182 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1183 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1184 SilcCommandStatus status;
1187 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1188 SILC_GET16_MSB(status, tmp);
1189 if (status != SILC_STATUS_OK) {
1190 cmd->client->ops->say(cmd->client, conn,
1191 "%s", silc_client_command_status_message(status));
1192 COMMAND_REPLY_ERROR;
1196 /* Notify application */
1197 COMMAND_REPLY((ARGS));
1199 /* Execute any pending command callbacks */
1200 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1203 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1204 silc_client_command_reply_free(cmd);
1207 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1209 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1210 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1211 SilcCommandStatus status;
1214 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1215 SILC_GET16_MSB(status, tmp);
1216 if (status != SILC_STATUS_OK) {
1217 cmd->client->ops->say(cmd->client, conn,
1218 "%s", silc_client_command_status_message(status));
1219 COMMAND_REPLY_ERROR;
1223 /* Notify application */
1224 COMMAND_REPLY((ARGS));
1226 /* Execute any pending command callbacks */
1227 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1230 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1231 silc_client_command_reply_free(cmd);
1234 /* Reply to LEAVE command. */
1236 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1238 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1239 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1240 SilcCommandStatus status;
1243 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1244 SILC_GET16_MSB(status, tmp);
1245 if (status != SILC_STATUS_OK) {
1246 cmd->client->ops->say(cmd->client, conn,
1247 "%s", silc_client_command_status_message(status));
1248 COMMAND_REPLY_ERROR;
1252 /* Notify application */
1253 COMMAND_REPLY((ARGS));
1255 /* Execute any pending command callbacks */
1256 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1259 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1260 silc_client_command_reply_free(cmd);
1263 /* Reply to USERS command. Received list of client ID's and theirs modes
1264 on the channel we requested. */
1266 SILC_CLIENT_CMD_REPLY_FUNC(users)
1268 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1269 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1270 SilcCommandStatus status;
1271 SilcIDCacheEntry id_cache = NULL;
1272 SilcChannelEntry channel;
1273 SilcChannelUser chu;
1274 SilcChannelID *channel_id = NULL;
1275 SilcBuffer client_id_list;
1276 SilcBuffer client_mode_list;
1278 unsigned int tmp_len, list_count;
1280 unsigned char **res_argv = NULL;
1281 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1283 SILC_LOG_DEBUG(("Start"));
1285 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1286 SILC_GET16_MSB(status, tmp);
1287 if (status != SILC_STATUS_OK) {
1288 cmd->client->ops->say(cmd->client, conn,
1289 "%s", silc_client_command_status_message(status));
1290 COMMAND_REPLY_ERROR;
1294 /* Get channel ID */
1295 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1298 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1302 /* Get the list count */
1303 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1306 SILC_GET32_MSB(list_count, tmp);
1308 /* Get Client ID list */
1309 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1313 client_id_list = silc_buffer_alloc(tmp_len);
1314 silc_buffer_pull_tail(client_id_list, tmp_len);
1315 silc_buffer_put(client_id_list, tmp, tmp_len);
1317 /* Get client mode list */
1318 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1322 client_mode_list = silc_buffer_alloc(tmp_len);
1323 silc_buffer_pull_tail(client_mode_list, tmp_len);
1324 silc_buffer_put(client_mode_list, tmp, tmp_len);
1326 /* Get channel entry */
1327 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1328 SILC_ID_CHANNEL, &id_cache)) {
1329 COMMAND_REPLY_ERROR;
1332 channel = (SilcChannelEntry)id_cache->context;
1334 /* Remove old client list from channel. */
1335 silc_list_start(channel->clients);
1336 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1337 silc_list_del(channel->clients, chu);
1341 /* Cache the received Client ID's and modes. This cache expires
1342 whenever server sends notify message to channel. It means two things;
1343 some user has joined or leaved the channel. XXX! */
1344 for (i = 0; i < list_count; i++) {
1345 unsigned short idp_len;
1347 SilcClientID *client_id;
1348 SilcClientEntry client;
1351 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1353 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1358 SILC_GET32_MSB(mode, client_mode_list->data);
1360 /* Check if we have this client cached already. */
1361 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1362 SILC_ID_CLIENT, &id_cache)) {
1363 /* No we don't have it, query it from the server. Assemble argument
1364 table that will be sent fr the IDENTIFY command later. */
1365 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1367 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1369 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1371 res_argv[res_argc] = client_id_list->data;
1372 res_argv_lens[res_argc] = idp_len;
1373 res_argv_types[res_argc] = res_argc + 3;
1376 /* Found the client, join it to the channel */
1377 client = (SilcClientEntry)id_cache->context;
1378 chu = silc_calloc(1, sizeof(*chu));
1379 chu->client = client;
1381 silc_list_add(channel->clients, chu);
1383 silc_free(client_id);
1387 silc_buffer_pull(client_id_list, idp_len);
1388 silc_buffer_pull(client_mode_list, 4);
1391 /* Query the client information from server if the list included clients
1392 that we don't know about. */
1396 /* Send the IDENTIFY command to server */
1397 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1398 res_argc, res_argv, res_argv_lens,
1399 res_argv_types, ++conn->cmd_ident);
1400 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1401 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1404 /* Register pending command callback. After we've received the IDENTIFY
1405 command reply we will reprocess this command reply by re-calling this
1406 USERS command reply callback. */
1407 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1408 NULL, silc_client_command_reply_users, cmd);
1410 silc_buffer_free(res_cmd);
1412 silc_free(channel_id);
1414 silc_free(res_argv);
1415 silc_free(res_argv_lens);
1416 silc_free(res_argv_types);
1420 /* Notify application */
1421 COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1423 /* Execute any pending command callbacks */
1424 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1426 silc_buffer_free(client_id_list);
1427 silc_buffer_free(client_mode_list);
1431 silc_free(channel_id);
1432 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1433 silc_client_command_reply_free(cmd);