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;
889 if (client_entry == conn->local_entry)
893 /* Join the client to the channel */
894 chu = silc_calloc(1, sizeof(*chu));
895 chu->client = client_entry;
897 silc_list_add(channel->clients, chu);
898 silc_free(client_id);
900 silc_buffer_pull(client_id_list, idp_len);
901 silc_buffer_pull(client_mode_list, 4);
903 silc_buffer_push(client_id_list, client_id_list->data -
904 client_id_list->head);
905 silc_buffer_push(client_mode_list, client_mode_list->data -
906 client_mode_list->head);
908 /* Save channel key */
909 silc_client_save_channel_key(conn, keyp, channel);
911 /* Notify application */
912 COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
913 NULL, topic, hmac, list_count, client_id_list,
916 /* Execute any pending command callbacks */
917 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
919 silc_buffer_free(keyp);
920 silc_buffer_free(client_id_list);
921 silc_buffer_free(client_mode_list);
924 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
925 silc_client_command_reply_free(cmd);
928 /* Received reply for MOTD command */
930 SILC_CLIENT_CMD_REPLY_FUNC(motd)
932 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
933 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
934 SilcCommandStatus status;
935 unsigned int argc, i;
937 char *motd = NULL, *cp, line[256];
939 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
940 SILC_GET16_MSB(status, tmp);
941 if (status != SILC_STATUS_OK) {
942 cmd->client->ops->say(cmd->client, conn,
943 "%s", silc_client_command_status_message(status));
948 argc = silc_argument_get_arg_num(cmd->args);
955 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
964 if (cp[i++] == '\n') {
965 memset(line, 0, sizeof(line));
966 strncat(line, cp, i - 1);
972 cmd->client->ops->say(cmd->client, conn, "%s", line);
981 /* Notify application */
982 COMMAND_REPLY((ARGS, motd));
984 /* Execute any pending command callbacks */
985 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
988 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
989 silc_client_command_reply_free(cmd);
992 SILC_CLIENT_CMD_REPLY_FUNC(umode)
996 /* Received reply for CMODE command. */
998 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1000 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1001 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1002 SilcCommandStatus status;
1005 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1006 if (status != SILC_STATUS_OK) {
1007 cmd->client->ops->say(cmd->client, conn,
1008 "%s", silc_client_command_status_message(status));
1009 COMMAND_REPLY_ERROR;
1013 /* Get channel mode */
1014 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1016 COMMAND_REPLY_ERROR;
1020 /* Notify application */
1021 COMMAND_REPLY((ARGS, tmp));
1023 /* Execute any pending command callbacks */
1024 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1027 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1028 silc_client_command_reply_free(cmd);
1031 /* Received reply for CUMODE command */
1033 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1035 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1036 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1037 SilcCommandStatus status;
1038 SilcIDCacheEntry id_cache = NULL;
1039 SilcClientID *client_id;
1040 unsigned char *tmp, *id;
1043 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1044 if (status != SILC_STATUS_OK) {
1045 cmd->client->ops->say(cmd->client, conn,
1046 "%s", silc_client_command_status_message(status));
1047 COMMAND_REPLY_ERROR;
1051 /* Get channel mode */
1052 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1054 COMMAND_REPLY_ERROR;
1059 id = silc_argument_get_arg_type(cmd->args, 3, &len);
1061 COMMAND_REPLY_ERROR;
1064 client_id = silc_id_payload_parse_id(id, len);
1066 COMMAND_REPLY_ERROR;
1070 /* Get client entry */
1071 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1072 SILC_ID_CLIENT, &id_cache)) {
1073 COMMAND_REPLY_ERROR;
1077 /* Notify application */
1078 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1079 silc_free(client_id);
1081 /* Execute any pending command callbacks */
1082 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1085 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1086 silc_client_command_reply_free(cmd);
1089 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1091 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1092 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1093 SilcCommandStatus status;
1096 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1097 SILC_GET16_MSB(status, tmp);
1098 if (status != SILC_STATUS_OK) {
1099 cmd->client->ops->say(cmd->client, conn,
1100 "%s", silc_client_command_status_message(status));
1101 COMMAND_REPLY_ERROR;
1105 /* Notify application */
1106 COMMAND_REPLY((ARGS));
1108 /* Execute any pending command callbacks */
1109 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1112 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1113 silc_client_command_reply_free(cmd);
1116 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1120 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1124 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1126 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1127 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1128 SilcCommandStatus status;
1131 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1132 SILC_GET16_MSB(status, tmp);
1133 if (status != SILC_STATUS_OK) {
1134 cmd->client->ops->say(cmd->client, conn,
1135 "%s", silc_client_command_status_message(status));
1136 COMMAND_REPLY_ERROR;
1140 /* Notify application */
1141 COMMAND_REPLY((ARGS));
1143 /* Execute any pending command callbacks */
1144 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1147 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1148 silc_client_command_reply_free(cmd);
1151 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1153 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1154 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1155 SilcCommandStatus status;
1158 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1159 SILC_GET16_MSB(status, tmp);
1160 if (status != SILC_STATUS_OK) {
1161 cmd->client->ops->say(cmd->client, conn,
1162 "%s", silc_client_command_status_message(status));
1163 COMMAND_REPLY_ERROR;
1167 /* Notify application */
1168 COMMAND_REPLY((ARGS));
1170 /* Execute any pending command callbacks */
1171 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1174 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1175 silc_client_command_reply_free(cmd);
1178 SILC_CLIENT_CMD_REPLY_FUNC(close)
1180 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1181 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1182 SilcCommandStatus status;
1185 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1186 SILC_GET16_MSB(status, tmp);
1187 if (status != SILC_STATUS_OK) {
1188 cmd->client->ops->say(cmd->client, conn,
1189 "%s", silc_client_command_status_message(status));
1190 COMMAND_REPLY_ERROR;
1194 /* Notify application */
1195 COMMAND_REPLY((ARGS));
1197 /* Execute any pending command callbacks */
1198 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1201 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1202 silc_client_command_reply_free(cmd);
1205 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1207 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1208 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1209 SilcCommandStatus status;
1212 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1213 SILC_GET16_MSB(status, tmp);
1214 if (status != SILC_STATUS_OK) {
1215 cmd->client->ops->say(cmd->client, conn,
1216 "%s", silc_client_command_status_message(status));
1217 COMMAND_REPLY_ERROR;
1221 /* Notify application */
1222 COMMAND_REPLY((ARGS));
1224 /* Execute any pending command callbacks */
1225 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1228 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1229 silc_client_command_reply_free(cmd);
1232 /* Reply to LEAVE command. */
1234 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1236 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1237 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1238 SilcCommandStatus status;
1241 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1242 SILC_GET16_MSB(status, tmp);
1243 if (status != SILC_STATUS_OK) {
1244 cmd->client->ops->say(cmd->client, conn,
1245 "%s", silc_client_command_status_message(status));
1246 COMMAND_REPLY_ERROR;
1250 /* Notify application */
1251 COMMAND_REPLY((ARGS));
1253 /* Execute any pending command callbacks */
1254 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1257 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1258 silc_client_command_reply_free(cmd);
1261 /* Reply to USERS command. Received list of client ID's and theirs modes
1262 on the channel we requested. */
1264 SILC_CLIENT_CMD_REPLY_FUNC(users)
1266 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1267 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1268 SilcCommandStatus status;
1269 SilcIDCacheEntry id_cache = NULL;
1270 SilcChannelEntry channel;
1271 SilcChannelUser chu;
1272 SilcChannelID *channel_id = NULL;
1273 SilcBuffer client_id_list;
1274 SilcBuffer client_mode_list;
1276 unsigned int tmp_len, list_count;
1278 unsigned char **res_argv = NULL;
1279 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1281 SILC_LOG_DEBUG(("Start"));
1283 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1284 SILC_GET16_MSB(status, tmp);
1285 if (status != SILC_STATUS_OK) {
1286 cmd->client->ops->say(cmd->client, conn,
1287 "%s", silc_client_command_status_message(status));
1288 COMMAND_REPLY_ERROR;
1292 /* Get channel ID */
1293 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1296 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1300 /* Get the list count */
1301 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1304 SILC_GET32_MSB(list_count, tmp);
1306 /* Get Client ID list */
1307 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1311 client_id_list = silc_buffer_alloc(tmp_len);
1312 silc_buffer_pull_tail(client_id_list, tmp_len);
1313 silc_buffer_put(client_id_list, tmp, tmp_len);
1315 /* Get client mode list */
1316 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1320 client_mode_list = silc_buffer_alloc(tmp_len);
1321 silc_buffer_pull_tail(client_mode_list, tmp_len);
1322 silc_buffer_put(client_mode_list, tmp, tmp_len);
1324 /* Get channel entry */
1325 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1326 SILC_ID_CHANNEL, &id_cache)) {
1327 COMMAND_REPLY_ERROR;
1330 channel = (SilcChannelEntry)id_cache->context;
1332 /* Remove old client list from channel. */
1333 silc_list_start(channel->clients);
1334 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1335 silc_list_del(channel->clients, chu);
1339 /* Cache the received Client ID's and modes. This cache expires
1340 whenever server sends notify message to channel. It means two things;
1341 some user has joined or leaved the channel. XXX! */
1342 for (i = 0; i < list_count; i++) {
1343 unsigned short idp_len;
1345 SilcClientID *client_id;
1346 SilcClientEntry client;
1349 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1351 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1356 SILC_GET32_MSB(mode, client_mode_list->data);
1358 /* Check if we have this client cached already. */
1359 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1360 SILC_ID_CLIENT, &id_cache)) {
1361 /* No we don't have it, query it from the server. Assemble argument
1362 table that will be sent fr the IDENTIFY command later. */
1363 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1365 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1367 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1369 res_argv[res_argc] = client_id_list->data;
1370 res_argv_lens[res_argc] = idp_len;
1371 res_argv_types[res_argc] = res_argc + 3;
1374 /* Found the client, join it to the channel */
1375 client = (SilcClientEntry)id_cache->context;
1376 chu = silc_calloc(1, sizeof(*chu));
1377 chu->client = client;
1379 silc_list_add(channel->clients, chu);
1381 silc_free(client_id);
1385 silc_buffer_pull(client_id_list, idp_len);
1386 silc_buffer_pull(client_mode_list, 4);
1389 /* Query the client information from server if the list included clients
1390 that we don't know about. */
1394 /* Send the IDENTIFY command to server */
1395 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1396 res_argc, res_argv, res_argv_lens,
1397 res_argv_types, ++conn->cmd_ident);
1398 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1399 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1402 /* Register pending command callback. After we've received the IDENTIFY
1403 command reply we will reprocess this command reply by re-calling this
1404 USERS command reply callback. */
1405 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1406 NULL, silc_client_command_reply_users, cmd);
1408 silc_buffer_free(res_cmd);
1410 silc_free(channel_id);
1412 silc_free(res_argv);
1413 silc_free(res_argv_lens);
1414 silc_free(res_argv_types);
1418 /* Notify application */
1419 COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1421 /* Execute any pending command callbacks */
1422 SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1424 silc_buffer_free(client_id_list);
1425 silc_buffer_free(client_mode_list);
1429 silc_free(channel_id);
1430 SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1431 silc_client_command_reply_free(cmd);