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 SilcCommandStatus status;
745 SilcIDPayload idp = NULL;
746 SilcChannelEntry channel;
747 SilcIDCacheEntry id_cache = NULL;
749 unsigned int argc, mode, len, list_count;
750 char *topic, *tmp, *channel_name = NULL, *hmac;
751 SilcBuffer keyp, client_id_list, client_mode_list;
754 SILC_LOG_DEBUG(("Start"));
756 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
757 if (status != SILC_STATUS_OK) {
758 cmd->client->ops->say(cmd->client, conn,
759 "%s", silc_client_command_status_message(status));
764 argc = silc_argument_get_arg_num(cmd->args);
765 if (argc < 7 || argc > 14) {
766 cmd->client->ops->say(cmd->client, conn,
767 "Cannot join channel: Bad reply packet");
772 /* Get channel name */
773 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
775 cmd->client->ops->say(cmd->client, conn,
776 "Cannot join channel: Bad reply packet");
780 channel_name = strdup(tmp);
783 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
785 cmd->client->ops->say(cmd->client, conn,
786 "Cannot join channel: Bad reply packet");
788 silc_free(channel_name);
791 idp = silc_id_payload_parse_data(tmp, len);
794 silc_free(channel_name);
798 /* Get channel mode */
799 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
801 SILC_GET32_MSB(mode, tmp);
805 /* Get channel key */
806 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
808 silc_id_payload_free(idp);
809 silc_free(channel_name);
812 keyp = silc_buffer_alloc(len);
813 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
814 silc_buffer_put(keyp, tmp, len);
817 topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
819 /* Save received Channel ID. This actually creates the channel */
820 channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
822 silc_id_payload_free(idp);
825 hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
827 if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
828 cmd->client->ops->say(cmd->client, conn,
829 "Cannot join channel: Unsupported HMAC `%s'",
832 silc_free(channel_name);
837 /* Get the list count */
838 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
841 SILC_GET32_MSB(list_count, tmp);
843 /* Get Client ID list */
844 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
848 client_id_list = silc_buffer_alloc(len);
849 silc_buffer_pull_tail(client_id_list, len);
850 silc_buffer_put(client_id_list, tmp, len);
852 /* Get client mode list */
853 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
857 client_mode_list = silc_buffer_alloc(len);
858 silc_buffer_pull_tail(client_mode_list, len);
859 silc_buffer_put(client_mode_list, tmp, len);
861 /* Add clients we received in the reply to the channel */
862 for (i = 0; i < list_count; i++) {
863 unsigned short idp_len;
865 SilcClientID *client_id;
866 SilcClientEntry client_entry;
869 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
871 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
876 SILC_GET32_MSB(mode, client_mode_list->data);
878 /* Check if we have this client cached already. */
879 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
880 SILC_ID_CLIENT, &id_cache)) {
881 /* No, we don't have it, add entry for it. */
882 client_entry = silc_calloc(1, sizeof(*client_entry));
883 client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
884 silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT,
885 client_entry->id, (void *)client_entry, FALSE);
887 /* Yes, we have it already */
888 client_entry = (SilcClientEntry)id_cache->context;
891 /* Join the client to the channel */
892 chu = silc_calloc(1, sizeof(*chu));
893 chu->client = client_entry;
895 silc_list_add(channel->clients, chu);
896 silc_free(client_id);
898 silc_buffer_pull(client_id_list, idp_len);
899 silc_buffer_pull(client_mode_list, 4);
901 silc_buffer_push(client_id_list, client_id_list->data -
902 client_id_list->head);
903 silc_buffer_push(client_mode_list, client_mode_list->data -
904 client_mode_list->head);
906 /* Save channel key */
907 silc_client_save_channel_key(conn, keyp, channel);
909 /* Notify application */
910 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
911 NULL, topic, hmac, list_count, client_id_list,
914 /* Execute any pending command callbacks */
915 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
917 silc_buffer_free(keyp);
918 silc_buffer_free(client_id_list);
919 silc_buffer_free(client_mode_list);
922 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
923 silc_client_command_reply_free(cmd);
926 /* Received reply for MOTD command */
928 SILC_CLIENT_CMD_REPLY_FUNC(motd)
930 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
931 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
932 SilcCommandStatus status;
933 unsigned int argc, i;
935 char *motd = NULL, *cp, line[256];
937 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
938 SILC_GET16_MSB(status, tmp);
939 if (status != SILC_STATUS_OK) {
940 cmd->client->ops->say(cmd->client, conn,
941 "%s", silc_client_command_status_message(status));
946 argc = silc_argument_get_arg_num(cmd->args);
953 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
962 if (cp[i++] == '\n') {
963 memset(line, 0, sizeof(line));
964 strncat(line, cp, i - 1);
970 cmd->client->ops->say(cmd->client, conn, "%s", line);
979 /* Notify application */
980 COMMAND_REPLY((ARGS, motd));
982 /* Execute any pending command callbacks */
983 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
986 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
987 silc_client_command_reply_free(cmd);
990 SILC_CLIENT_CMD_REPLY_FUNC(umode)
994 /* Received reply for CMODE command. */
996 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
998 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
999 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1000 SilcCommandStatus status;
1003 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1004 if (status != SILC_STATUS_OK) {
1005 cmd->client->ops->say(cmd->client, conn,
1006 "%s", silc_client_command_status_message(status));
1007 COMMAND_REPLY_ERROR;
1011 /* Get channel mode */
1012 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1014 COMMAND_REPLY_ERROR;
1018 /* Notify application */
1019 COMMAND_REPLY((ARGS, tmp));
1021 /* Execute any pending command callbacks */
1022 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1025 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1026 silc_client_command_reply_free(cmd);
1029 /* Received reply for CUMODE command */
1031 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1033 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1034 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1035 SilcCommandStatus status;
1036 SilcIDCacheEntry id_cache = NULL;
1037 SilcClientID *client_id;
1038 unsigned char *tmp, *id;
1041 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1042 if (status != SILC_STATUS_OK) {
1043 cmd->client->ops->say(cmd->client, conn,
1044 "%s", silc_client_command_status_message(status));
1045 COMMAND_REPLY_ERROR;
1049 /* Get channel mode */
1050 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1052 COMMAND_REPLY_ERROR;
1057 id = silc_argument_get_arg_type(cmd->args, 3, &len);
1059 COMMAND_REPLY_ERROR;
1062 client_id = silc_id_payload_parse_id(id, len);
1064 COMMAND_REPLY_ERROR;
1068 /* Get client entry */
1069 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1070 SILC_ID_CLIENT, &id_cache)) {
1071 COMMAND_REPLY_ERROR;
1075 /* Notify application */
1076 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1077 silc_free(client_id);
1079 /* Execute any pending command callbacks */
1080 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1083 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1084 silc_client_command_reply_free(cmd);
1087 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1089 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1090 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1091 SilcCommandStatus status;
1094 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1095 SILC_GET16_MSB(status, tmp);
1096 if (status != SILC_STATUS_OK) {
1097 cmd->client->ops->say(cmd->client, conn,
1098 "%s", silc_client_command_status_message(status));
1099 COMMAND_REPLY_ERROR;
1103 /* Notify application */
1104 COMMAND_REPLY((ARGS));
1106 /* Execute any pending command callbacks */
1107 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1110 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1111 silc_client_command_reply_free(cmd);
1114 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1118 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1122 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1124 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1125 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1126 SilcCommandStatus status;
1129 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1130 SILC_GET16_MSB(status, tmp);
1131 if (status != SILC_STATUS_OK) {
1132 cmd->client->ops->say(cmd->client, conn,
1133 "%s", silc_client_command_status_message(status));
1134 COMMAND_REPLY_ERROR;
1138 /* Notify application */
1139 COMMAND_REPLY((ARGS));
1141 /* Execute any pending command callbacks */
1142 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1145 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1146 silc_client_command_reply_free(cmd);
1149 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1151 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1152 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1153 SilcCommandStatus status;
1156 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1157 SILC_GET16_MSB(status, tmp);
1158 if (status != SILC_STATUS_OK) {
1159 cmd->client->ops->say(cmd->client, conn,
1160 "%s", silc_client_command_status_message(status));
1161 COMMAND_REPLY_ERROR;
1165 /* Notify application */
1166 COMMAND_REPLY((ARGS));
1168 /* Execute any pending command callbacks */
1169 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1172 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1173 silc_client_command_reply_free(cmd);
1176 SILC_CLIENT_CMD_REPLY_FUNC(close)
1178 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1179 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1180 SilcCommandStatus status;
1183 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1184 SILC_GET16_MSB(status, tmp);
1185 if (status != SILC_STATUS_OK) {
1186 cmd->client->ops->say(cmd->client, conn,
1187 "%s", silc_client_command_status_message(status));
1188 COMMAND_REPLY_ERROR;
1192 /* Notify application */
1193 COMMAND_REPLY((ARGS));
1195 /* Execute any pending command callbacks */
1196 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1199 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1200 silc_client_command_reply_free(cmd);
1203 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1205 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1206 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1207 SilcCommandStatus status;
1210 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1211 SILC_GET16_MSB(status, tmp);
1212 if (status != SILC_STATUS_OK) {
1213 cmd->client->ops->say(cmd->client, conn,
1214 "%s", silc_client_command_status_message(status));
1215 COMMAND_REPLY_ERROR;
1219 /* Notify application */
1220 COMMAND_REPLY((ARGS));
1222 /* Execute any pending command callbacks */
1223 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1226 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1227 silc_client_command_reply_free(cmd);
1230 /* Reply to LEAVE command. */
1232 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1234 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1235 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1236 SilcCommandStatus status;
1239 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1240 SILC_GET16_MSB(status, tmp);
1241 if (status != SILC_STATUS_OK) {
1242 cmd->client->ops->say(cmd->client, conn,
1243 "%s", silc_client_command_status_message(status));
1244 COMMAND_REPLY_ERROR;
1248 /* Notify application */
1249 COMMAND_REPLY((ARGS));
1251 /* Execute any pending command callbacks */
1252 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1255 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1256 silc_client_command_reply_free(cmd);
1259 /* Reply to USERS command. Received list of client ID's and theirs modes
1260 on the channel we requested. */
1262 SILC_CLIENT_CMD_REPLY_FUNC(users)
1264 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1265 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1266 SilcCommandStatus status;
1267 SilcIDCacheEntry id_cache = NULL;
1268 SilcChannelEntry channel;
1269 SilcChannelUser chu;
1270 SilcChannelID *channel_id = NULL;
1271 SilcBuffer client_id_list;
1272 SilcBuffer client_mode_list;
1274 unsigned int tmp_len, list_count;
1276 unsigned char **res_argv = NULL;
1277 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1279 SILC_LOG_DEBUG(("Start"));
1281 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1282 SILC_GET16_MSB(status, tmp);
1283 if (status != SILC_STATUS_OK) {
1284 cmd->client->ops->say(cmd->client, conn,
1285 "%s", silc_client_command_status_message(status));
1286 COMMAND_REPLY_ERROR;
1290 /* Get channel ID */
1291 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1294 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1298 /* Get the list count */
1299 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1302 SILC_GET32_MSB(list_count, tmp);
1304 /* Get Client ID list */
1305 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1309 client_id_list = silc_buffer_alloc(tmp_len);
1310 silc_buffer_pull_tail(client_id_list, tmp_len);
1311 silc_buffer_put(client_id_list, tmp, tmp_len);
1313 /* Get client mode list */
1314 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1318 client_mode_list = silc_buffer_alloc(tmp_len);
1319 silc_buffer_pull_tail(client_mode_list, tmp_len);
1320 silc_buffer_put(client_mode_list, tmp, tmp_len);
1322 /* Get channel entry */
1323 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1324 SILC_ID_CHANNEL, &id_cache)) {
1325 COMMAND_REPLY_ERROR;
1328 channel = (SilcChannelEntry)id_cache->context;
1330 /* Remove old client list from channel. */
1331 silc_list_start(channel->clients);
1332 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1333 silc_list_del(channel->clients, chu);
1337 /* Cache the received Client ID's and modes. This cache expires
1338 whenever server sends notify message to channel. It means two things;
1339 some user has joined or leaved the channel. XXX! */
1340 for (i = 0; i < list_count; i++) {
1341 unsigned short idp_len;
1343 SilcClientID *client_id;
1344 SilcClientEntry client;
1347 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1349 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1354 SILC_GET32_MSB(mode, client_mode_list->data);
1356 /* Check if we have this client cached already. */
1357 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1358 SILC_ID_CLIENT, &id_cache)) {
1359 /* No we don't have it, query it from the server. Assemble argument
1360 table that will be sent fr the IDENTIFY command later. */
1361 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1363 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1365 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1367 res_argv[res_argc] = client_id_list->data;
1368 res_argv_lens[res_argc] = idp_len;
1369 res_argv_types[res_argc] = res_argc + 3;
1372 /* Found the client, join it to the channel */
1373 client = (SilcClientEntry)id_cache->context;
1374 chu = silc_calloc(1, sizeof(*chu));
1375 chu->client = client;
1377 silc_list_add(channel->clients, chu);
1379 silc_free(client_id);
1383 silc_buffer_pull(client_id_list, idp_len);
1384 silc_buffer_pull(client_mode_list, 4);
1387 /* Query the client information from server if the list included clients
1388 that we don't know about. */
1392 /* Send the IDENTIFY command to server */
1393 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1394 res_argc, res_argv, res_argv_lens,
1395 res_argv_types, ++conn->cmd_ident);
1396 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1397 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1400 /* Register pending command callback. After we've received the IDENTIFY
1401 command reply we will reprocess this command reply by re-calling this
1402 USERS command reply callback. */
1403 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1404 NULL, silc_client_command_reply_users, cmd);
1406 silc_buffer_free(res_cmd);
1408 silc_free(channel_id);
1410 silc_free(res_argv);
1411 silc_free(res_argv_lens);
1412 silc_free(res_argv_types);
1416 /* Notify application */
1417 COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1419 /* Execute any pending command callbacks */
1420 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1422 silc_buffer_free(client_id_list);
1423 silc_buffer_free(client_mode_list);
1427 silc_free(channel_id);
1428 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1429 silc_client_command_reply_free(cmd);