5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 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"
38 /* Client command reply list. */
39 SilcClientCommandReply silc_command_reply_list[] =
41 SILC_CLIENT_CMD_REPLY(whois, WHOIS),
42 SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
43 SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
44 SILC_CLIENT_CMD_REPLY(nick, NICK),
45 SILC_CLIENT_CMD_REPLY(list, LIST),
46 SILC_CLIENT_CMD_REPLY(topic, TOPIC),
47 SILC_CLIENT_CMD_REPLY(invite, INVITE),
48 SILC_CLIENT_CMD_REPLY(quit, QUIT),
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(die, DIE),
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_SERVER_PRIV), "Permission denied. You are not server operator" },
111 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
112 { STAT(BAD_NICKNAME), "Bad nickname" },
113 { STAT(BAD_CHANNEL), "Bad channel name" },
114 { STAT(AUTH_FAILED), "Authentication failed" },
119 /* Command reply operation that is called at the end of all command replys.
120 Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
121 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
122 #define ARGS cmd->client, cmd->sock->user_data, \
123 cmd->payload, TRUE, silc_command_get(cmd->payload), status
125 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
126 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
127 cmd->sock->user_data, cmd->payload, FALSE, \
128 silc_command_get(cmd->payload), status)
130 /* Process received command reply. */
132 void silc_client_command_reply_process(SilcClient client,
133 SilcSocketConnection sock,
134 SilcPacketContext *packet)
136 SilcBuffer buffer = packet->buffer;
137 SilcClientCommandReply *cmd;
138 SilcClientCommandReplyContext ctx;
139 SilcCommandPayload payload;
141 unsigned short ident;
143 /* Get command reply payload from packet */
144 payload = silc_command_payload_parse(buffer);
146 /* Silently ignore bad reply packet */
147 SILC_LOG_DEBUG(("Bad command reply packet"));
151 /* Allocate command reply context. This must be free'd by the
152 command reply routine receiving it. */
153 ctx = silc_calloc(1, sizeof(*ctx));
154 ctx->client = client;
156 ctx->payload = payload;
157 ctx->args = silc_command_get_args(ctx->payload);
158 ctx->packet = packet;
159 ident = silc_command_get_ident(ctx->payload);
161 /* Check for pending commands and mark to be exeucted */
162 silc_client_command_pending_check(sock->user_data, ctx,
163 silc_command_get(ctx->payload), ident);
165 /* Execute command reply */
166 command = silc_command_get(ctx->payload);
167 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
168 if (cmd->cmd == command)
171 if (cmd == NULL || !cmd->cb) {
179 /* Returns status message string */
182 silc_client_command_status_message(SilcCommandStatus status)
186 for (i = 0; silc_command_status_messages[i].message; i++) {
187 if (silc_command_status_messages[i].status == status)
191 if (silc_command_status_messages[i].message == NULL)
194 return silc_command_status_messages[i].message;
197 /* Free command reply context and its internals. */
199 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
202 silc_command_free_payload(cmd->payload);
208 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
209 SilcCommandStatus status)
213 unsigned char *id_data;
214 char *nickname = NULL, *username = NULL;
215 char *realname = NULL;
216 SilcClientID *client_id;
217 SilcIDCacheEntry id_cache = NULL;
218 SilcClientEntry client_entry = NULL;
219 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
221 memset(buf, 0, sizeof(buf));
223 argc = silc_argument_get_arg_num(cmd->args);
225 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
231 client_id = silc_id_payload_parse_id(id_data, len);
237 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
239 strncat(buf, nickname, len);
240 strncat(buf, " is ", 4);
243 username = silc_argument_get_arg_type(cmd->args, 4, &len);
245 strncat(buf, username, len);
248 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
250 strncat(buf, " (", 2);
251 strncat(buf, realname, len);
252 strncat(buf, ")", 1);
255 /* Check if we have this client cached already. */
256 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
257 SILC_ID_CLIENT, &id_cache)) {
258 client_entry = silc_calloc(1, sizeof(*client_entry));
259 client_entry->id = client_id;
260 silc_parse_nickname(nickname, &client_entry->nickname,
261 &client_entry->server, &client_entry->num);
262 client_entry->username = strdup(username);
264 client_entry->realname = strdup(realname);
266 /* Add client to cache */
267 silc_idcache_add(conn->client_cache, client_entry->nickname,
268 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
270 client_entry = (SilcClientEntry)id_cache->context;
271 if (client_entry->nickname)
272 silc_free(client_entry->nickname);
273 if (client_entry->server)
274 silc_free(client_entry->server);
275 if (client_entry->username)
276 silc_free(client_entry->username);
277 if (client_entry->realname)
278 silc_free(client_entry->realname);
280 silc_parse_nickname(nickname, &client_entry->nickname,
281 &client_entry->server, &client_entry->num);
282 client_entry->username = strdup(username);
284 client_entry->realname = strdup(realname);
286 id_cache->data = client_entry->nickname;
287 silc_idcache_sort_by_data(conn->client_cache);
289 silc_free(client_id);
293 cmd->client->ops->say(cmd->client, conn, "%s", buf);
295 /* Notify application */
296 COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
300 /* Received reply for WHOIS command. This maybe called several times
301 for one WHOIS command as server may reply with list of results. */
303 SILC_CLIENT_CMD_REPLY_FUNC(whois)
305 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
306 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
307 SilcCommandStatus status;
310 SILC_LOG_DEBUG(("Start"));
312 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
313 SILC_GET16_MSB(status, tmp);
314 if (status != SILC_STATUS_OK &&
315 status != SILC_STATUS_LIST_START &&
316 status != SILC_STATUS_LIST_ITEM &&
317 status != SILC_STATUS_LIST_END) {
318 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
319 /* Take nickname which may be provided */
320 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
322 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
323 silc_client_command_status_message(status));
325 cmd->client->ops->say(cmd->client, conn, "%s",
326 silc_client_command_status_message(status));
330 cmd->client->ops->say(cmd->client, conn,
331 "%s", silc_client_command_status_message(status));
337 /* Display one whois reply */
338 if (status == SILC_STATUS_OK) {
339 silc_client_command_reply_whois_print(cmd, status);
342 /* XXX list should not be displayed untill all items has been received. */
343 if (status == SILC_STATUS_LIST_START) {
344 silc_client_command_reply_whois_print(cmd, status);
347 if (status == SILC_STATUS_LIST_ITEM) {
348 silc_client_command_reply_whois_print(cmd, status);
351 if (status == SILC_STATUS_LIST_END) {
352 silc_client_command_reply_whois_print(cmd, status);
355 /* Execute any pending command callbacks */
356 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
359 silc_client_command_reply_free(cmd);
362 /* Received reply for WHOWAS command. */
364 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
369 /* Received reply for IDENTIFY command. This maybe called several times
370 for one IDENTIFY command as server may reply with list of results.
371 This is totally silent and does not print anything on screen. */
373 SILC_CLIENT_CMD_REPLY_FUNC(identify)
375 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
376 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
377 SilcClientEntry client_entry;
378 SilcIDCacheEntry id_cache = NULL;
379 SilcCommandStatus status;
382 SILC_LOG_DEBUG(("Start"));
384 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
385 SILC_GET16_MSB(status, tmp);
386 if (status != SILC_STATUS_OK) {
387 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
388 /* Take nickname which may be provided */
389 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
391 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
392 silc_client_command_status_message(status));
394 cmd->client->ops->say(cmd->client, conn, "%s",
395 silc_client_command_status_message(status));
399 cmd->client->ops->say(cmd->client, conn,
400 "%s", silc_client_command_status_message(status));
406 /* Display one whois reply */
407 if (status == SILC_STATUS_OK) {
409 unsigned char *id_data;
412 SilcClientID *client_id;
414 id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
417 client_id = silc_id_payload_parse_id(id_data, len);
421 nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
422 username = silc_argument_get_arg_type(cmd->args, 4, NULL);
424 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
425 SILC_ID_CLIENT, &id_cache)) {
426 client_entry = silc_calloc(1, sizeof(*client_entry));
427 client_entry->id = client_id;
428 silc_parse_nickname(nickname, &client_entry->nickname,
429 &client_entry->server, &client_entry->num);
431 client_entry->username = strdup(username);
433 /* Add client to cache */
434 silc_idcache_add(conn->client_cache, client_entry->nickname,
435 SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
437 client_entry = (SilcClientEntry)id_cache->context;
438 if (client_entry->nickname)
439 silc_free(client_entry->nickname);
440 if (client_entry->server)
441 silc_free(client_entry->server);
442 if (username && client_entry->username)
443 silc_free(client_entry->username);
445 silc_parse_nickname(nickname, &client_entry->nickname,
446 &client_entry->server, &client_entry->num);
449 client_entry->username = strdup(username);
451 id_cache->data = client_entry->nickname;
452 silc_idcache_sort_by_data(conn->client_cache);
454 silc_free(client_id);
458 if (status == SILC_STATUS_LIST_START) {
462 if (status == SILC_STATUS_LIST_END) {
466 /* Execute any pending command callbacks */
467 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
470 silc_client_command_reply_free(cmd);
473 /* Received reply for command NICK. If everything went without errors
474 we just received our new Client ID. */
476 SILC_CLIENT_CMD_REPLY_FUNC(nick)
478 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
479 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
480 SilcCommandStatus status;
483 unsigned int argc, len;
485 SILC_LOG_DEBUG(("Start"));
487 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
488 if (status != SILC_STATUS_OK) {
489 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
490 silc_client_command_status_message(status));
495 argc = silc_argument_get_arg_num(cmd->args);
496 if (argc < 2 || argc > 2) {
497 cmd->client->ops->say(cmd->client, conn,
498 "Cannot set nickname: bad reply to command");
503 /* Take received Client ID */
504 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
505 idp = silc_id_payload_parse_data(tmp, len);
510 silc_client_receive_new_id(cmd->client, cmd->sock, idp);
512 /* Notify application */
513 COMMAND_REPLY((ARGS, conn->local_entry));
515 /* Execute any pending command callbacks */
516 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
519 silc_client_command_reply_free(cmd);
522 SILC_CLIENT_CMD_REPLY_FUNC(list)
526 /* Received reply to topic command. */
528 SILC_CLIENT_CMD_REPLY_FUNC(topic)
530 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
531 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
532 SilcCommandStatus status;
533 SilcChannelEntry channel;
534 SilcChannelID *channel_id = NULL;
535 SilcIDCacheEntry id_cache = NULL;
538 unsigned int argc, len;
540 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
541 if (status != SILC_STATUS_OK) {
542 cmd->client->ops->say(cmd->client, conn,
543 "%s", silc_client_command_status_message(status));
545 silc_client_command_reply_free(cmd);
549 argc = silc_argument_get_arg_num(cmd->args);
550 if (argc < 1 || argc > 3) {
555 /* Take Channel ID */
556 tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
561 topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
565 channel_id = silc_id_payload_parse_id(tmp, len);
569 /* Get the channel name */
570 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
571 SILC_ID_CHANNEL, &id_cache)) {
572 silc_free(channel_id);
577 channel = (SilcChannelEntry)id_cache->context;
579 cmd->client->ops->say(cmd->client, conn,
580 "Topic on channel %s: %s", channel->channel_name,
583 /* Notify application */
584 COMMAND_REPLY((ARGS, channel, topic));
586 /* Execute any pending command callbacks */
587 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
590 silc_client_command_reply_free(cmd);
593 /* Received reply to invite command. */
595 SILC_CLIENT_CMD_REPLY_FUNC(invite)
597 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
598 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
599 SilcCommandStatus status;
602 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
603 SILC_GET16_MSB(status, tmp);
604 if (status != SILC_STATUS_OK) {
605 cmd->client->ops->say(cmd->client, conn,
606 "%s", silc_client_command_status_message(status));
608 silc_client_command_reply_free(cmd);
612 /* Notify application */
613 COMMAND_REPLY((ARGS));
615 /* Execute any pending command callbacks */
616 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
618 silc_client_command_reply_free(cmd);
621 SILC_CLIENT_CMD_REPLY_FUNC(quit)
625 SILC_CLIENT_CMD_REPLY_FUNC(kill)
629 /* Received reply to INFO command. We receive the server ID and some
630 information about the server user requested. */
632 SILC_CLIENT_CMD_REPLY_FUNC(info)
634 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
635 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
636 SilcClient client = cmd->client;
637 SilcCommandStatus status;
640 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
641 SILC_GET16_MSB(status, tmp);
642 if (status != SILC_STATUS_OK) {
643 cmd->client->ops->say(cmd->client, conn,
644 "%s", silc_client_command_status_message(status));
646 silc_client_command_reply_free(cmd);
651 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
655 /* XXX save server id */
657 /* Get server info */
658 tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
662 client->ops->say(cmd->client, conn, "Info: %s", tmp);
664 /* Notify application */
665 COMMAND_REPLY((ARGS, NULL, (char *)tmp));
667 /* Execute any pending command callbacks */
668 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
671 silc_client_command_reply_free(cmd);
674 SILC_CLIENT_CMD_REPLY_FUNC(connect)
678 /* Received reply to PING command. The reply time is shown to user. */
680 SILC_CLIENT_CMD_REPLY_FUNC(ping)
682 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
683 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
684 SilcCommandStatus status;
687 time_t diff, curtime;
689 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
690 if (status != SILC_STATUS_OK) {
691 cmd->client->ops->say(cmd->client, conn,
692 "%s", silc_client_command_status_message(status));
697 curtime = time(NULL);
698 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
699 cmd->packet->src_id_type);
705 for (i = 0; i < conn->ping_count; i++) {
706 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
707 diff = curtime - conn->ping[i].start_time;
708 cmd->client->ops->say(cmd->client, conn,
709 "Ping reply from %s: %d second%s",
710 conn->ping[i].dest_name, diff,
711 diff == 1 ? "" : "s");
713 conn->ping[i].start_time = 0;
714 silc_free(conn->ping[i].dest_id);
715 conn->ping[i].dest_id = NULL;
716 silc_free(conn->ping[i].dest_name);
717 conn->ping[i].dest_name = NULL;
724 /* Notify application */
725 COMMAND_REPLY((ARGS));
727 /* Execute any pending command callbacks */
728 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
731 silc_client_command_reply_free(cmd);
734 SILC_CLIENT_CMD_REPLY_FUNC(oper)
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 unsigned int argc, mode, len;
748 char *topic, *tmp, *channel_name = NULL;
751 SILC_LOG_DEBUG(("Start"));
753 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
754 if (status != SILC_STATUS_OK) {
755 cmd->client->ops->say(cmd->client, conn,
756 "%s", silc_client_command_status_message(status));
761 argc = silc_argument_get_arg_num(cmd->args);
762 if (argc < 3 || argc > 9) {
763 cmd->client->ops->say(cmd->client, conn,
764 "Cannot join channel: Bad reply packet");
769 /* Get channel name */
770 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
772 cmd->client->ops->say(cmd->client, conn,
773 "Cannot join channel: Bad reply packet");
777 channel_name = strdup(tmp);
780 tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
782 cmd->client->ops->say(cmd->client, conn,
783 "Cannot join channel: Bad reply packet");
785 silc_free(channel_name);
788 idp = silc_id_payload_parse_data(tmp, len);
791 silc_free(channel_name);
795 /* Get channel mode */
796 tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
798 SILC_GET32_MSB(mode, tmp);
802 /* Get channel key */
803 tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
805 silc_id_payload_free(idp);
806 silc_free(channel_name);
809 keyp = silc_buffer_alloc(len);
810 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
811 silc_buffer_put(keyp, tmp, len);
814 topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
816 /* Save received Channel ID */
817 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
819 silc_id_payload_free(idp);
821 /* Save channel key */
822 silc_client_save_channel_key(conn, keyp, conn->current_channel);
823 silc_buffer_free(keyp);
826 client->ops->say(cmd->client, conn,
827 "Topic for %s: %s", channel_name, topic);
829 /* Notify application */
830 COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
833 /* Execute any pending command callbacks */
834 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
837 silc_client_command_reply_free(cmd);
840 /* Received reply for MOTD command */
842 SILC_CLIENT_CMD_REPLY_FUNC(motd)
844 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
845 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
846 SilcCommandStatus status;
847 unsigned int argc, i;
849 char *motd = NULL, *cp, line[256];
851 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
852 SILC_GET16_MSB(status, tmp);
853 if (status != SILC_STATUS_OK) {
854 cmd->client->ops->say(cmd->client, conn,
855 "%s", silc_client_command_status_message(status));
860 argc = silc_argument_get_arg_num(cmd->args);
867 motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
876 if (cp[i++] == '\n') {
877 memset(line, 0, sizeof(line));
878 strncat(line, cp, i - 1);
884 cmd->client->ops->say(cmd->client, conn, "%s", line);
893 /* Notify application */
894 COMMAND_REPLY((ARGS, motd));
896 /* Execute any pending command callbacks */
897 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
900 silc_client_command_reply_free(cmd);
903 SILC_CLIENT_CMD_REPLY_FUNC(umode)
907 /* Received reply for CMODE command. */
909 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
911 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
912 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
913 SilcCommandStatus status;
916 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
917 if (status != SILC_STATUS_OK) {
918 cmd->client->ops->say(cmd->client, conn,
919 "%s", silc_client_command_status_message(status));
924 /* Get channel mode */
925 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
931 /* Notify application */
932 COMMAND_REPLY((ARGS, tmp));
934 /* Execute any pending command callbacks */
935 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
938 silc_client_command_reply_free(cmd);
941 /* Received reply for CUMODE command */
943 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
945 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
946 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
947 SilcCommandStatus status;
948 SilcIDCacheEntry id_cache = NULL;
949 SilcClientID *client_id;
950 unsigned char *tmp, *id;
953 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
954 if (status != SILC_STATUS_OK) {
955 cmd->client->ops->say(cmd->client, conn,
956 "%s", silc_client_command_status_message(status));
961 /* Get channel mode */
962 tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
969 id = silc_argument_get_arg_type(cmd->args, 3, &len);
974 client_id = silc_id_payload_parse_id(id, len);
980 /* Get client entry */
981 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
982 SILC_ID_CLIENT, &id_cache)) {
987 /* Notify application */
988 COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
989 silc_free(client_id);
991 /* Execute any pending command callbacks */
992 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
995 silc_client_command_reply_free(cmd);
998 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1002 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1006 SILC_CLIENT_CMD_REPLY_FUNC(close)
1010 SILC_CLIENT_CMD_REPLY_FUNC(die)
1014 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1018 /* Reply to LEAVE command. */
1020 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1022 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1023 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1024 SilcCommandStatus status;
1027 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1028 SILC_GET16_MSB(status, tmp);
1029 if (status != SILC_STATUS_OK) {
1030 cmd->client->ops->say(cmd->client, conn,
1031 "%s", silc_client_command_status_message(status));
1032 COMMAND_REPLY_ERROR;
1036 /* Notify application */
1037 COMMAND_REPLY((ARGS));
1039 /* Execute any pending command callbacks */
1040 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
1042 silc_client_command_reply_free(cmd);
1045 /* Reply to USERS command. Received list of client ID's and theirs modes
1046 on the channel we requested. */
1048 SILC_CLIENT_CMD_REPLY_FUNC(users)
1050 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1051 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1052 SilcCommandStatus status;
1053 SilcIDCacheEntry id_cache = NULL;
1054 SilcChannelEntry channel;
1055 SilcChannelUser chu;
1056 SilcChannelID *channel_id = NULL;
1057 SilcBuffer client_id_list;
1058 SilcBuffer client_mode_list;
1060 unsigned int tmp_len, list_count;
1062 unsigned char **res_argv = NULL;
1063 unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1065 SILC_LOG_DEBUG(("Start"));
1067 tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1068 SILC_GET16_MSB(status, tmp);
1069 if (status != SILC_STATUS_OK) {
1070 cmd->client->ops->say(cmd->client, conn,
1071 "%s", silc_client_command_status_message(status));
1072 COMMAND_REPLY_ERROR;
1076 /* Get channel ID */
1077 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1080 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1084 /* Get the list count */
1085 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1088 SILC_GET32_MSB(list_count, tmp);
1090 /* Get Client ID list */
1091 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1095 client_id_list = silc_buffer_alloc(tmp_len);
1096 silc_buffer_pull_tail(client_id_list, tmp_len);
1097 silc_buffer_put(client_id_list, tmp, tmp_len);
1099 /* Get client mode list */
1100 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1104 client_mode_list = silc_buffer_alloc(tmp_len);
1105 silc_buffer_pull_tail(client_mode_list, tmp_len);
1106 silc_buffer_put(client_mode_list, tmp, tmp_len);
1108 /* Get channel entry */
1109 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1110 SILC_ID_CHANNEL, &id_cache)) {
1111 COMMAND_REPLY_ERROR;
1114 channel = (SilcChannelEntry)id_cache->context;
1116 /* Remove old client list from channel. */
1117 silc_list_start(channel->clients);
1118 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1119 silc_list_del(channel->clients, chu);
1123 /* Cache the received Client ID's and modes. This cache expires
1124 whenever server sends notify message to channel. It means two things;
1125 some user has joined or leaved the channel. XXX! */
1126 for (i = 0; i < list_count; i++) {
1127 unsigned short idp_len;
1129 SilcClientID *client_id;
1130 SilcClientEntry client;
1133 SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1135 client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1140 SILC_GET32_MSB(mode, client_mode_list->data);
1142 /* Check if we have this client cached already. */
1143 if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1144 SILC_ID_CLIENT, &id_cache)) {
1145 /* No we don't have it, query it from the server. Assemble argument
1146 table that will be sent fr the IDENTIFY command later. */
1147 res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1149 res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1151 res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1153 res_argv[res_argc] = client_id_list->data;
1154 res_argv_lens[res_argc] = idp_len;
1155 res_argv_types[res_argc] = res_argc + 3;
1158 /* Found the client, join it to the channel */
1159 client = (SilcClientEntry)id_cache->context;
1160 chu = silc_calloc(1, sizeof(*chu));
1161 chu->client = client;
1163 silc_list_add(channel->clients, chu);
1165 silc_free(client_id);
1169 silc_buffer_pull(client_id_list, idp_len);
1170 silc_buffer_pull(client_mode_list, 4);
1173 /* Query the client information from server if the list included clients
1174 that we don't know about. */
1178 /* Send the IDENTIFY command to server */
1179 res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1180 res_argc, res_argv, res_argv_lens,
1181 res_argv_types, ++conn->cmd_ident);
1182 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1183 NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1186 /* Register pending command callback. After we've received the IDENTIFY
1187 command reply we will reprocess this command reply by re-calling this
1188 USERS command reply callback. */
1189 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1190 silc_client_command_reply_users, cmd);
1192 silc_buffer_free(res_cmd);
1194 silc_free(channel_id);
1196 silc_free(res_argv);
1197 silc_free(res_argv_lens);
1198 silc_free(res_argv_types);
1202 /* We have all the clients on the channel cached now. Create a nice
1203 output for user interface and notify application. */
1205 if (!cmd->callback) {
1206 /* Server has sent us USERS reply even when we haven't actually sent
1207 USERS command. This is normal behaviour when joining to a channel.
1208 Display some nice information on the user interface. */
1209 int k = 0, len1 = 0, len2 = 0;
1210 char *name_list = NULL;
1212 silc_list_start(channel->clients);
1213 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1214 char *m, *n = chu->client->nickname;
1218 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1220 m = silc_client_chumode_char(chu->mode);
1222 memcpy(name_list + (len1 - len2), m, strlen(m));
1227 memcpy(name_list + (len1 - len2), n, len2);
1228 name_list[len1] = 0;
1230 if (k == silc_list_count(channel->clients) - 1)
1232 memcpy(name_list + len1, " ", 1);
1237 cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1238 channel->channel_name, name_list);
1239 silc_free(name_list);
1242 /* Notify application */
1243 COMMAND_REPLY((ARGS, channel, client_id_list->head,
1244 client_mode_list->head));
1246 /* Execute any pending command callbacks */
1247 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
1249 silc_buffer_free(client_id_list);
1250 silc_buffer_free(client_mode_list);
1254 silc_free(channel_id);
1255 silc_client_command_reply_free(cmd);