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 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
391 /* Take nickname which may be provided */
392 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
394 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
395 silc_client_command_status_message(status));
397 cmd->client->ops->say(cmd->client, conn, "%s",
398 silc_client_command_status_message(status));
402 cmd->client->ops->say(cmd->client, conn,
403 "%s", silc_client_command_status_message(status));
409 /* Display one whois reply */
410 if (status == SILC_STATUS_OK) {
412 unsigned char *id_data;
415 SilcClientID *client_id;
417 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
420 client_id = silc_id_payload_parse_id(id_data, len);
424 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
425 username = silc_argument_get_arg_type(cmd->args, 4, NULL);
427 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
428 SILC_ID_CLIENT, &id_cache)) {
429 client_entry = silc_calloc(1, sizeof(*client_entry));
430 client_entry->id = client_id;
431 silc_parse_nickname(nickname, &client_entry->nickname,
432 &client_entry->server, &client_entry->num);
434 client_entry->username = strdup(username);
436 /* Add client to cache */
437 silc_idcache_add(conn->client_cache, client_entry->nickname,
438 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
440 client_entry = (SilcClientEntry)id_cache->context;
441 if (client_entry->nickname)
442 silc_free(client_entry->nickname);
443 if (client_entry->server)
444 silc_free(client_entry->server);
445 if (username && client_entry->username)
446 silc_free(client_entry->username);
448 silc_parse_nickname(nickname, &client_entry->nickname,
449 &client_entry->server, &client_entry->num);
452 client_entry->username = strdup(username);
454 id_cache->data = client_entry->nickname;
455 silc_idcache_sort_by_data(conn->client_cache);
457 silc_free(client_id);
461 if (status == SILC_STATUS_LIST_START) {
465 if (status == SILC_STATUS_LIST_END) {
469 /* Execute any pending command callbacks */
470 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
473 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
474 silc_client_command_reply_free(cmd);
477 /* Received reply for command NICK. If everything went without errors
478 we just received our new Client ID. */
480 SILC_CLIENT_CMD_REPLY_FUNC(nick)
482 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
483 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
484 SilcCommandStatus status;
487 unsigned int argc, len;
489 SILC_LOG_DEBUG(("Start"));
491 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
492 if (status != SILC_STATUS_OK) {
493 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
494 silc_client_command_status_message(status));
499 argc = silc_argument_get_arg_num(cmd->args);
500 if (argc < 2 || argc > 2) {
501 cmd->client->ops->say(cmd->client, conn,
502 "Cannot set nickname: bad reply to command");
507 /* Take received Client ID */
508 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
509 idp = silc_id_payload_parse_data(tmp, len);
514 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
516 /* Notify application */
517 COMMAND_REPLY((ARGS, conn->local_entry));
519 /* Execute any pending command callbacks */
520 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
523 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
524 silc_client_command_reply_free(cmd);
527 SILC_CLIENT_CMD_REPLY_FUNC(list)
531 /* Received reply to topic command. */
533 SILC_CLIENT_CMD_REPLY_FUNC(topic)
535 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
536 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
537 SilcCommandStatus status;
538 SilcChannelEntry channel;
539 SilcChannelID *channel_id = NULL;
540 SilcIDCacheEntry id_cache = NULL;
543 unsigned int argc, len;
545 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
546 if (status != SILC_STATUS_OK) {
547 cmd->client->ops->say(cmd->client, conn,
548 "%s", silc_client_command_status_message(status));
550 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
551 silc_client_command_reply_free(cmd);
555 argc = silc_argument_get_arg_num(cmd->args);
556 if (argc < 1 || argc > 3) {
561 /* Take Channel ID */
562 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
567 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
571 channel_id = silc_id_payload_parse_id(tmp, len);
575 /* Get the channel name */
576 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
577 SILC_ID_CHANNEL, &id_cache)) {
578 silc_free(channel_id);
583 channel = (SilcChannelEntry)id_cache->context;
585 cmd->client->ops->say(cmd->client, conn,
586 "Topic on channel %s: %s", channel->channel_name,
589 /* Notify application */
590 COMMAND_REPLY((ARGS, channel, topic));
592 /* Execute any pending command callbacks */
593 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
596 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
597 silc_client_command_reply_free(cmd);
600 /* Received reply to invite command. */
602 SILC_CLIENT_CMD_REPLY_FUNC(invite)
604 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
605 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
606 SilcCommandStatus status;
609 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
610 SILC_GET16_MSB(status, tmp);
611 if (status != SILC_STATUS_OK) {
612 cmd->client->ops->say(cmd->client, conn,
613 "%s", silc_client_command_status_message(status));
615 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
616 silc_client_command_reply_free(cmd);
620 /* Notify application */
621 COMMAND_REPLY((ARGS));
623 /* Execute any pending command callbacks */
624 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
626 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
627 silc_client_command_reply_free(cmd);
630 SILC_CLIENT_CMD_REPLY_FUNC(kill)
634 /* Received reply to INFO command. We receive the server ID and some
635 information about the server user requested. */
637 SILC_CLIENT_CMD_REPLY_FUNC(info)
639 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
640 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
641 SilcClient client = cmd->client;
642 SilcCommandStatus status;
645 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
646 SILC_GET16_MSB(status, tmp);
647 if (status != SILC_STATUS_OK) {
648 cmd->client->ops->say(cmd->client, conn,
649 "%s", silc_client_command_status_message(status));
651 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
652 silc_client_command_reply_free(cmd);
657 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
661 /* XXX save server id */
663 /* Get server info */
664 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
668 client->ops->say(cmd->client, conn, "Info: %s", tmp);
670 /* Notify application */
671 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
673 /* Execute any pending command callbacks */
674 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
677 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
678 silc_client_command_reply_free(cmd);
681 /* Received reply to PING command. The reply time is shown to user. */
683 SILC_CLIENT_CMD_REPLY_FUNC(ping)
685 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
686 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
687 SilcCommandStatus status;
690 time_t diff, curtime;
692 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
693 if (status != SILC_STATUS_OK) {
694 cmd->client->ops->say(cmd->client, conn,
695 "%s", silc_client_command_status_message(status));
700 curtime = time(NULL);
701 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
702 cmd->packet->src_id_type);
708 for (i = 0; i < conn->ping_count; i++) {
709 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
710 diff = curtime - conn->ping[i].start_time;
711 cmd->client->ops->say(cmd->client, conn,
712 "Ping reply from %s: %d second%s",
713 conn->ping[i].dest_name, diff,
714 diff == 1 ? "" : "s");
716 conn->ping[i].start_time = 0;
717 silc_free(conn->ping[i].dest_id);
718 conn->ping[i].dest_id = NULL;
719 silc_free(conn->ping[i].dest_name);
720 conn->ping[i].dest_name = NULL;
727 /* Notify application */
728 COMMAND_REPLY((ARGS));
730 /* Execute any pending command callbacks */
731 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
734 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
735 silc_client_command_reply_free(cmd);
738 /* Received reply for JOIN command. */
740 SILC_CLIENT_CMD_REPLY_FUNC(join)
742 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
743 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
744 SilcClient client = cmd->client;
745 SilcCommandStatus status;
746 SilcIDPayload idp = NULL;
747 SilcChannelEntry channel;
748 unsigned int argc, mode, len;
749 char *topic, *tmp, *channel_name = NULL, *hmac;
752 SILC_LOG_DEBUG(("Start"));
754 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
755 if (status != SILC_STATUS_OK) {
756 cmd->client->ops->say(cmd->client, conn,
757 "%s", silc_client_command_status_message(status));
762 argc = silc_argument_get_arg_num(cmd->args);
763 if (argc < 3 || argc > 9) {
764 cmd->client->ops->say(cmd->client, conn,
765 "Cannot join channel: Bad reply packet");
770 /* Get channel name */
771 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
773 cmd->client->ops->say(cmd->client, conn,
774 "Cannot join channel: Bad reply packet");
778 channel_name = strdup(tmp);
781 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
783 cmd->client->ops->say(cmd->client, conn,
784 "Cannot join channel: Bad reply packet");
786 silc_free(channel_name);
789 idp = silc_id_payload_parse_data(tmp, len);
792 silc_free(channel_name);
796 /* Get channel mode */
797 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
799 SILC_GET32_MSB(mode, tmp);
803 /* Get channel key */
804 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
806 silc_id_payload_free(idp);
807 silc_free(channel_name);
810 keyp = silc_buffer_alloc(len);
811 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
812 silc_buffer_put(keyp, tmp, len);
815 topic = silc_argument_get_arg_type(cmd->args, 9, NULL);
817 /* Save received Channel ID. This actually creates the channel */
818 channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
820 silc_id_payload_free(idp);
823 hmac = silc_argument_get_arg_type(cmd->args, 10, NULL);
825 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
826 cmd->client->ops->say(cmd->client, conn,
827 "Cannot join channel: Unsupported HMAC `%s'",
830 silc_free(channel_name);
835 /* Save channel key */
836 silc_client_save_channel_key(conn, keyp, channel);
837 silc_buffer_free(keyp);
840 client->ops->say(cmd->client, conn,
841 "Topic for %s: %s", channel_name, topic);
843 /* Notify application */
844 COMMAND_REPLY((ARGS, channel_name, channel, mode, NULL, NULL, topic, hmac));
846 /* Execute any pending command callbacks */
847 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
850 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
851 silc_client_command_reply_free(cmd);
854 /* Received reply for MOTD command */
856 SILC_CLIENT_CMD_REPLY_FUNC(motd)
858 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
859 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
860 SilcCommandStatus status;
861 unsigned int argc, i;
863 char *motd = NULL, *cp, line[256];
865 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
866 SILC_GET16_MSB(status, tmp);
867 if (status != SILC_STATUS_OK) {
868 cmd->client->ops->say(cmd->client, conn,
869 "%s", silc_client_command_status_message(status));
874 argc = silc_argument_get_arg_num(cmd->args);
881 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
890 if (cp[i++] == '\n') {
891 memset(line, 0, sizeof(line));
892 strncat(line, cp, i - 1);
898 cmd->client->ops->say(cmd->client, conn, "%s", line);
907 /* Notify application */
908 COMMAND_REPLY((ARGS, motd));
910 /* Execute any pending command callbacks */
911 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
914 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
915 silc_client_command_reply_free(cmd);
918 SILC_CLIENT_CMD_REPLY_FUNC(umode)
922 /* Received reply for CMODE command. */
924 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
926 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
927 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
928 SilcCommandStatus status;
931 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
932 if (status != SILC_STATUS_OK) {
933 cmd->client->ops->say(cmd->client, conn,
934 "%s", silc_client_command_status_message(status));
939 /* Get channel mode */
940 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
946 /* Notify application */
947 COMMAND_REPLY((ARGS, tmp));
949 /* Execute any pending command callbacks */
950 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
953 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
954 silc_client_command_reply_free(cmd);
957 /* Received reply for CUMODE command */
959 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
961 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
962 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
963 SilcCommandStatus status;
964 SilcIDCacheEntry id_cache = NULL;
965 SilcClientID *client_id;
966 unsigned char *tmp, *id;
969 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
970 if (status != SILC_STATUS_OK) {
971 cmd->client->ops->say(cmd->client, conn,
972 "%s", silc_client_command_status_message(status));
977 /* Get channel mode */
978 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
985 id = silc_argument_get_arg_type(cmd->args, 3, &len);
990 client_id = silc_id_payload_parse_id(id, len);
996 /* Get client entry */
997 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
998 SILC_ID_CLIENT, &id_cache)) {
1003 /* Notify application */
1004 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1005 silc_free(client_id);
1007 /* Execute any pending command callbacks */
1008 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1011 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1012 silc_client_command_reply_free(cmd);
1015 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1017 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1018 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1019 SilcCommandStatus status;
1022 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1023 SILC_GET16_MSB(status, tmp);
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 /* Notify application */
1032 COMMAND_REPLY((ARGS));
1034 /* Execute any pending command callbacks */
1035 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1038 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1039 silc_client_command_reply_free(cmd);
1042 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1046 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1050 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1052 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1053 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1054 SilcCommandStatus status;
1057 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1058 SILC_GET16_MSB(status, tmp);
1059 if (status != SILC_STATUS_OK) {
1060 cmd->client->ops->say(cmd->client, conn,
1061 "%s", silc_client_command_status_message(status));
1062 COMMAND_REPLY_ERROR;
1066 /* Notify application */
1067 COMMAND_REPLY((ARGS));
1069 /* Execute any pending command callbacks */
1070 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1073 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1074 silc_client_command_reply_free(cmd);
1077 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1079 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1080 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1081 SilcCommandStatus status;
1084 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1085 SILC_GET16_MSB(status, tmp);
1086 if (status != SILC_STATUS_OK) {
1087 cmd->client->ops->say(cmd->client, conn,
1088 "%s", silc_client_command_status_message(status));
1089 COMMAND_REPLY_ERROR;
1093 /* Notify application */
1094 COMMAND_REPLY((ARGS));
1096 /* Execute any pending command callbacks */
1097 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1100 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1101 silc_client_command_reply_free(cmd);
1104 SILC_CLIENT_CMD_REPLY_FUNC(close)
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_CLOSE);
1127 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1128 silc_client_command_reply_free(cmd);
1131 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
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_SHUTDOWN);
1154 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1155 silc_client_command_reply_free(cmd);
1158 /* Reply to LEAVE command. */
1160 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1162 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1163 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1164 SilcCommandStatus status;
1167 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1168 SILC_GET16_MSB(status, tmp);
1169 if (status != SILC_STATUS_OK) {
1170 cmd->client->ops->say(cmd->client, conn,
1171 "%s", silc_client_command_status_message(status));
1172 COMMAND_REPLY_ERROR;
1176 /* Notify application */
1177 COMMAND_REPLY((ARGS));
1179 /* Execute any pending command callbacks */
1180 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1183 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1184 silc_client_command_reply_free(cmd);
1187 /* Reply to USERS command. Received list of client ID's and theirs modes
1188 on the channel we requested. */
1190 SILC_CLIENT_CMD_REPLY_FUNC(users)
1192 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1193 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1194 SilcCommandStatus status;
1195 SilcIDCacheEntry id_cache = NULL;
1196 SilcChannelEntry channel;
1197 SilcChannelUser chu;
1198 SilcChannelID *channel_id = NULL;
1199 SilcBuffer client_id_list;
1200 SilcBuffer client_mode_list;
1202 unsigned int tmp_len, list_count;
1204 unsigned char **res_argv = NULL;
1205 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1207 SILC_LOG_DEBUG(("Start"));
1209 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1210 SILC_GET16_MSB(status, tmp);
1211 if (status != SILC_STATUS_OK) {
1212 cmd->client->ops->say(cmd->client, conn,
1213 "%s", silc_client_command_status_message(status));
1214 COMMAND_REPLY_ERROR;
1218 /* Get channel ID */
1219 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1222 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1226 /* Get the list count */
1227 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1230 SILC_GET32_MSB(list_count, tmp);
1232 /* Get Client ID list */
1233 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1237 client_id_list = silc_buffer_alloc(tmp_len);
1238 silc_buffer_pull_tail(client_id_list, tmp_len);
1239 silc_buffer_put(client_id_list, tmp, tmp_len);
1241 /* Get client mode list */
1242 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1246 client_mode_list = silc_buffer_alloc(tmp_len);
1247 silc_buffer_pull_tail(client_mode_list, tmp_len);
1248 silc_buffer_put(client_mode_list, tmp, tmp_len);
1250 /* Get channel entry */
1251 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1252 SILC_ID_CHANNEL, &id_cache)) {
1253 COMMAND_REPLY_ERROR;
1256 channel = (SilcChannelEntry)id_cache->context;
1258 /* Remove old client list from channel. */
1259 silc_list_start(channel->clients);
1260 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1261 silc_list_del(channel->clients, chu);
1265 /* Cache the received Client ID's and modes. This cache expires
1266 whenever server sends notify message to channel. It means two things;
1267 some user has joined or leaved the channel. XXX! */
1268 for (i = 0; i < list_count; i++) {
1269 unsigned short idp_len;
1271 SilcClientID *client_id;
1272 SilcClientEntry client;
1275 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1277 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1282 SILC_GET32_MSB(mode, client_mode_list->data);
1284 /* Check if we have this client cached already. */
1285 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1286 SILC_ID_CLIENT, &id_cache)) {
1287 /* No we don't have it, query it from the server. Assemble argument
1288 table that will be sent fr the IDENTIFY command later. */
1289 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1291 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1293 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1295 res_argv[res_argc] = client_id_list->data;
1296 res_argv_lens[res_argc] = idp_len;
1297 res_argv_types[res_argc] = res_argc + 3;
1300 /* Found the client, join it to the channel */
1301 client = (SilcClientEntry)id_cache->context;
1302 chu = silc_calloc(1, sizeof(*chu));
1303 chu->client = client;
1305 silc_list_add(channel->clients, chu);
1307 silc_free(client_id);
1311 silc_buffer_pull(client_id_list, idp_len);
1312 silc_buffer_pull(client_mode_list, 4);
1315 /* Query the client information from server if the list included clients
1316 that we don't know about. */
1320 /* Send the IDENTIFY command to server */
1321 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1322 res_argc, res_argv, res_argv_lens,
1323 res_argv_types, ++conn->cmd_ident);
1324 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1325 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1328 /* Register pending command callback. After we've received the IDENTIFY
1329 command reply we will reprocess this command reply by re-calling this
1330 USERS command reply callback. */
1331 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1332 NULL, silc_client_command_reply_users, cmd);
1334 silc_buffer_free(res_cmd);
1336 silc_free(channel_id);
1338 silc_free(res_argv);
1339 silc_free(res_argv_lens);
1340 silc_free(res_argv_types);
1344 /* We have all the clients on the channel cached now. Create a nice
1345 output for user interface and notify application. */
1347 if (!cmd->callback) {
1348 /* Server has sent us USERS reply even when we haven't actually sent
1349 USERS command. This is normal behaviour when joining to a channel.
1350 Display some nice information on the user interface. */
1351 int k = 0, len1 = 0, len2 = 0;
1352 char *name_list = NULL;
1354 silc_list_start(channel->clients);
1355 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1356 char *m, *n = chu->client->nickname;
1360 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1362 m = silc_client_chumode_char(chu->mode);
1364 memcpy(name_list + (len1 - len2), m, strlen(m));
1369 memcpy(name_list + (len1 - len2), n, len2);
1370 name_list[len1] = 0;
1372 if (k == silc_list_count(channel->clients) - 1)
1374 memcpy(name_list + len1, " ", 1);
1379 cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1380 channel->channel_name, name_list);
1381 silc_free(name_list);
1384 /* Notify application */
1385 COMMAND_REPLY((ARGS, channel, client_id_list->head,
1386 client_mode_list->head));
1388 /* Execute any pending command callbacks */
1389 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1391 silc_buffer_free(client_id_list);
1392 silc_buffer_free(client_mode_list);
1396 silc_free(channel_id);
1397 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1398 silc_client_command_reply_free(cmd);