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.
22 #include "clientlibincludes.h"
23 #include "client_internal.h"
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
28 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
31 SILC_CF_LAG | SILC_CF_REG, 3),
32 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37 SILC_CLIENT_CMD(kill, KILL, "KILL",
38 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43 SILC_CLIENT_CMD(oper, OPER, "OPER",
44 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
52 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
54 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
55 SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
56 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
57 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
58 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
59 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
60 SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
62 { NULL, 0, NULL, 0, 0 },
65 #define SILC_NOT_CONNECTED(x, c) \
66 x->ops->say((x), (c), \
67 "You are not connected to a server, use /SERVER to connect");
69 /* Command operation that is called at the end of all commands.
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72 cmd, TRUE, cmd->command->cmd)
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76 cmd, FALSE, cmd->command->cmd)
78 /* Generic function to send any command. The arguments must be sent already
79 encoded into correct form and in correct order. */
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82 SilcCommand command, unsigned short ident,
83 unsigned int argc, ...)
90 packet = silc_command_payload_encode_vap(command, ident, argc, ap);
91 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
92 NULL, 0, NULL, NULL, packet->data,
94 silc_buffer_free(packet);
97 /* Finds and returns a pointer to the command list. Return NULL if the
98 command is not found. */
100 SilcClientCommand *silc_client_command_find(const char *name)
102 SilcClientCommand *cmd;
104 for (cmd = silc_command_list; cmd->name; cmd++) {
105 if (!strcmp(cmd->name, name))
112 /* Add new pending command to be executed when reply to a command has been
113 received. The `reply_cmd' is the command that will call the `callback'
114 with `context' when reply has been received. If `ident is non-zero
115 the `callback' will be executed when received reply with command
116 identifier `ident'. */
118 void silc_client_command_pending(SilcClientConnection conn,
119 SilcCommand reply_cmd,
120 unsigned short ident,
121 SilcClientPendingDestructor destructor,
122 SilcCommandCb callback,
125 SilcClientCommandPending *reply;
127 reply = silc_calloc(1, sizeof(*reply));
128 reply->reply_cmd = reply_cmd;
129 reply->ident = ident;
130 reply->context = context;
131 reply->callback = callback;
132 reply->destructor = destructor;
133 silc_dlist_add(conn->pending_commands, reply);
136 /* Deletes pending command by reply command type. */
138 void silc_client_command_pending_del(SilcClientConnection conn,
139 SilcCommand reply_cmd,
140 unsigned short ident)
142 SilcClientCommandPending *r;
144 silc_dlist_start(conn->pending_commands);
145 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
146 if (r->reply_cmd == reply_cmd && r->ident == ident) {
147 silc_dlist_del(conn->pending_commands, r);
153 /* Checks for pending commands and marks callbacks to be called from
154 the command reply function. Returns TRUE if there were pending command. */
156 int silc_client_command_pending_check(SilcClientConnection conn,
157 SilcClientCommandReplyContext ctx,
159 unsigned short ident)
161 SilcClientCommandPending *r;
163 silc_dlist_start(conn->pending_commands);
164 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
165 if (r->reply_cmd == command && r->ident == ident) {
166 ctx->context = r->context;
167 ctx->callback = r->callback;
168 ctx->destructor = r->destructor;
177 /* Allocate Command Context */
179 SilcClientCommandContext silc_client_command_alloc()
181 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
186 /* Free command context and its internals */
188 void silc_client_command_free(SilcClientCommandContext ctx)
191 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
193 if (ctx->users < 1) {
196 for (i = 0; i < ctx->argc; i++)
197 silc_free(ctx->argv[i]);
202 /* Duplicate Command Context by adding reference counter. The context won't
203 be free'd untill it hits zero. */
205 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
208 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
213 /* Pending command destructor. */
215 static void silc_client_command_destructor(void *context)
217 silc_client_command_free((SilcClientCommandContext)context);
220 /* silc_client_get_client completion callback */
221 void silc_client_command_completion(SilcClient client,
222 SilcClientConnection conn,
223 SilcClientEntry clients,
224 unsigned int clients_count,
230 /* Command WHOIS. This command is used to query information about
233 SILC_CLIENT_CMD_FUNC(whois)
235 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
236 SilcClientConnection conn = cmd->conn;
240 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
245 if (cmd->argc < 2 || cmd->argc > 3) {
246 cmd->client->ops->say(cmd->client, conn,
247 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
252 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
253 cmd->argc - 1, ++cmd->argv,
254 ++cmd->argv_lens, ++cmd->argv_types,
256 silc_client_packet_send(cmd->client, cmd->conn->sock,
257 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
258 buffer->data, buffer->len, TRUE);
259 silc_buffer_free(buffer);
264 /* Notify application */
268 silc_client_command_free(cmd);
271 /* Command WHOWAS. This command is used to query history information about
272 specific user that used to exist in the network. */
274 SILC_CLIENT_CMD_FUNC(whowas)
276 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
277 SilcClientConnection conn = cmd->conn;
281 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
286 if (cmd->argc < 2 || cmd->argc > 3) {
287 cmd->client->ops->say(cmd->client, conn,
288 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
293 buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
294 cmd->argc - 1, ++cmd->argv,
295 ++cmd->argv_lens, ++cmd->argv_types,
297 silc_client_packet_send(cmd->client, cmd->conn->sock,
298 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
299 buffer->data, buffer->len, TRUE);
300 silc_buffer_free(buffer);
305 /* Notify application */
309 silc_client_command_free(cmd);
312 /* Command IDENTIFY. This command is used to query information about
313 specific user, especially ID's. */
315 SILC_CLIENT_CMD_FUNC(identify)
317 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
318 SilcClientConnection conn = cmd->conn;
322 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
327 if (cmd->argc < 2 || cmd->argc > 3) {
328 cmd->client->ops->say(cmd->client, conn,
329 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
334 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
335 cmd->argc - 1, ++cmd->argv,
336 ++cmd->argv_lens, ++cmd->argv_types,
338 silc_client_packet_send(cmd->client, cmd->conn->sock,
339 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
340 buffer->data, buffer->len, TRUE);
341 silc_buffer_free(buffer);
346 /* Notify application */
350 silc_client_command_free(cmd);
353 /* Command NICK. Shows current nickname/sets new nickname on current
356 SILC_CLIENT_CMD_FUNC(nick)
358 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
359 SilcClientConnection conn = cmd->conn;
363 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
368 if (!strcmp(conn->nickname, cmd->argv[1]))
371 /* Show current nickname */
374 cmd->client->ops->say(cmd->client, conn,
375 "Your nickname is %s on server %s",
376 conn->nickname, conn->remote_host);
378 cmd->client->ops->say(cmd->client, conn,
379 "Your nickname is %s", conn->nickname);
382 /* XXX Notify application */
387 /* Set new nickname */
388 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
389 cmd->argc - 1, ++cmd->argv,
390 ++cmd->argv_lens, ++cmd->argv_types,
391 ++cmd->conn->cmd_ident);
392 silc_client_packet_send(cmd->client, cmd->conn->sock,
393 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
394 buffer->data, buffer->len, TRUE);
395 silc_buffer_free(buffer);
400 silc_free(conn->nickname);
401 conn->nickname = strdup(cmd->argv[1]);
403 /* Notify application */
407 silc_client_command_free(cmd);
410 /* Command LIST. Lists channels on the current server. */
412 SILC_CLIENT_CMD_FUNC(list)
414 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
415 SilcClientConnection conn = cmd->conn;
416 SilcIDCacheEntry id_cache = NULL;
417 SilcChannelEntry channel;
418 SilcBuffer buffer, idp = NULL;
422 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
427 if (cmd->argc == 2) {
430 /* Get the Channel ID of the channel */
431 if (silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
432 channel = (SilcChannelEntry)id_cache->context;
433 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
438 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
439 ++conn->cmd_ident, 0);
441 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
442 ++conn->cmd_ident, 1,
443 1, idp->data, idp->len);
445 silc_client_packet_send(cmd->client, cmd->conn->sock,
446 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
447 buffer->data, buffer->len, TRUE);
448 silc_buffer_free(buffer);
450 silc_buffer_free(idp);
452 /* Notify application */
456 silc_client_command_free(cmd);
459 /* Command TOPIC. Sets/shows topic on a channel. */
461 SILC_CLIENT_CMD_FUNC(topic)
463 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
464 SilcClientConnection conn = cmd->conn;
465 SilcIDCacheEntry id_cache = NULL;
466 SilcChannelEntry channel;
467 SilcBuffer buffer, idp;
471 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
476 if (cmd->argc < 2 || cmd->argc > 3) {
477 cmd->client->ops->say(cmd->client, conn,
478 "Usage: /TOPIC <channel> [<topic>]");
483 if (cmd->argv[1][0] == '*') {
484 if (!conn->current_channel) {
485 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
489 name = conn->current_channel->channel_name;
494 if (!conn->current_channel) {
495 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
500 /* Get the Channel ID of the channel */
501 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
502 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
507 channel = (SilcChannelEntry)id_cache->context;
509 /* Send TOPIC command to the server */
510 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
512 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
513 1, idp->data, idp->len,
515 strlen(cmd->argv[2]));
517 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
518 1, idp->data, idp->len,
520 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
521 0, NULL, NULL, buffer->data, buffer->len, TRUE);
522 silc_buffer_free(buffer);
523 silc_buffer_free(idp);
525 /* Notify application */
529 silc_client_command_free(cmd);
532 /* Command INVITE. Invites specific client to join a channel. */
534 SILC_CLIENT_CMD_FUNC(invite)
536 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
537 SilcClient client = cmd->client;
538 SilcClientConnection conn = cmd->conn;
539 SilcClientEntry client_entry;
540 SilcChannelEntry channel_entry;
541 SilcBuffer buffer, clidp, chidp;
542 unsigned int num = 0;
543 char *nickname = NULL, *server = NULL;
546 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
551 if (cmd->argc != 3) {
552 cmd->client->ops->say(cmd->client, conn,
553 "Usage: /INVITE <nickname>[@<server>] <channel>");
558 /* Parse the typed nickname. */
559 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
560 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
565 /* Find client entry */
566 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
574 /* Client entry not found, it was requested thus mark this to be
576 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
577 silc_client_command_destructor,
578 silc_client_command_invite,
579 silc_client_command_dup(cmd));
584 /* Find channel entry */
585 channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
586 if (!channel_entry) {
587 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
593 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
594 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
595 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
596 1, clidp->data, clidp->len,
597 2, chidp->data, chidp->len);
598 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
599 0, NULL, NULL, buffer->data, buffer->len, TRUE);
600 silc_buffer_free(buffer);
601 silc_buffer_free(clidp);
602 silc_buffer_free(chidp);
604 cmd->client->ops->say(cmd->client, conn,
605 "Inviting %s to channel %s", cmd->argv[1],
608 /* Notify application */
616 silc_client_command_free(cmd);
621 SilcClientConnection conn;
624 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
626 QuitInternal q = (QuitInternal)context;
628 /* Close connection */
629 q->client->ops->disconnect(q->client, q->conn);
630 silc_client_close_connection(q->client, q->conn->sock->user_data);
635 /* Command QUIT. Closes connection with current server. */
637 SILC_CLIENT_CMD_FUNC(quit)
639 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
644 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
650 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
651 &cmd->argv[1], &cmd->argv_lens[1],
652 &cmd->argv_types[1], 0);
654 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
655 NULL, NULL, NULL, 0);
656 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
658 buffer->data, buffer->len, TRUE);
659 silc_buffer_free(buffer);
661 q = silc_calloc(1, sizeof(*q));
662 q->client = cmd->client;
665 /* We quit the connection with little timeout */
666 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
667 silc_client_command_quit_cb, (void *)q,
668 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
670 /* Notify application */
674 silc_client_command_free(cmd);
677 /* Command KILL. Router operator can use this command to remove an client
678 fromthe SILC Network. */
680 SILC_CLIENT_CMD_FUNC(kill)
682 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
683 SilcClientConnection conn = cmd->conn;
684 SilcBuffer buffer, idp;
685 SilcClientEntry target;
686 unsigned int num = 0;
687 char *nickname = NULL, *server = NULL;
690 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
696 cmd->client->ops->say(cmd->client, conn,
697 "Usage: /KILL <nickname> [<comment>]");
702 /* Parse the typed nickname. */
703 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
704 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
709 /* Get the target client */
710 target = silc_idlist_get_client(cmd->client, conn, nickname,
717 /* Client entry not found, it was requested thus mark this to be
719 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
721 silc_client_command_destructor,
722 silc_client_command_kill,
723 silc_client_command_dup(cmd));
728 /* Send the KILL command to the server */
729 idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
731 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1,
732 1, idp->data, idp->len);
734 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2,
735 1, idp->data, idp->len,
737 strlen(cmd->argv[2]));
738 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
739 0, NULL, NULL, buffer->data, buffer->len, TRUE);
740 silc_buffer_free(buffer);
741 silc_buffer_free(idp);
743 /* Notify application */
746 /* Remove the client entry to be killed */
747 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
749 if (target->nickname)
750 silc_free(target->nickname);
752 silc_free(target->server);
754 silc_free(target->id);
755 if (target->send_key)
756 silc_cipher_free(target->send_key);
757 if (target->receive_key)
758 silc_cipher_free(target->receive_key);
766 silc_client_command_free(cmd);
769 /* Command INFO. Request information about specific server. If specific
770 server is not provided the current server is used. */
772 SILC_CLIENT_CMD_FUNC(info)
774 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
775 SilcClientConnection conn = cmd->conn;
780 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
786 name = strdup(conn->remote_host);
788 name = strdup(cmd->argv[1]);
790 /* Send the command */
791 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
792 1, name, strlen(name));
793 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
794 0, NULL, NULL, buffer->data, buffer->len, TRUE);
795 silc_buffer_free(buffer);
797 /* Notify application */
801 silc_client_command_free(cmd);
804 /* Command PING. Sends ping to server. This is used to test the
805 communication channel. */
807 SILC_CLIENT_CMD_FUNC(ping)
809 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
810 SilcClientConnection conn = cmd->conn;
817 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
822 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
823 name = strdup(conn->remote_host);
825 /* Send the command */
826 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
827 1, conn->remote_id_data,
829 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
830 0, NULL, NULL, buffer->data, buffer->len, TRUE);
831 silc_buffer_free(buffer);
833 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
836 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
841 /* Start counting time */
842 for (i = 0; i < conn->ping_count; i++) {
843 if (conn->ping[i].dest_id == NULL) {
844 conn->ping[i].start_time = time(NULL);
845 conn->ping[i].dest_id = id;
846 conn->ping[i].dest_name = name;
851 if (i >= conn->ping_count) {
852 i = conn->ping_count;
853 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
854 conn->ping[i].start_time = time(NULL);
855 conn->ping[i].dest_id = id;
856 conn->ping[i].dest_name = name;
860 /* Notify application */
864 silc_client_command_free(cmd);
867 SILC_CLIENT_CMD_FUNC(notice)
871 /* Command JOIN. Joins to a channel. */
873 SILC_CLIENT_CMD_FUNC(join)
875 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
876 SilcClientConnection conn = cmd->conn;
877 SilcIDCacheEntry id_cache = NULL;
878 SilcBuffer buffer, idp;
881 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
887 /* Show channels currently joined to */
892 /* See if we have joined to the requested channel already */
893 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
895 cmd->client->ops->say(cmd->client, conn,
896 "You are talking to channel %s", cmd->argv[1]);
897 conn->current_channel = (SilcChannelEntry)id_cache->context;
899 cmd->client->screen->bottom_line->channel = cmd->argv[1];
900 silc_screen_print_bottom_line(cmd->client->screen, 0);
905 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
907 /* Send JOIN command to the server */
910 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
911 1, cmd->argv[1], cmd->argv_lens[1],
912 2, idp->data, idp->len);
913 else if (cmd->argc == 3)
916 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
917 1, cmd->argv[1], cmd->argv_lens[1],
918 2, idp->data, idp->len,
919 3, cmd->argv[2], cmd->argv_lens[2]);
922 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
923 1, cmd->argv[1], cmd->argv_lens[1],
924 2, idp->data, idp->len,
925 3, cmd->argv[2], cmd->argv_lens[2],
926 4, cmd->argv[3], cmd->argv_lens[3]);
928 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
929 0, NULL, NULL, buffer->data, buffer->len, TRUE);
930 silc_buffer_free(buffer);
931 silc_buffer_free(idp);
933 /* Notify application */
937 silc_client_command_free(cmd);
940 /* MOTD command. Requests motd from server. */
942 SILC_CLIENT_CMD_FUNC(motd)
944 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
945 SilcClientConnection conn = cmd->conn;
949 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
954 if (cmd->argc < 1 || cmd->argc > 2) {
955 cmd->client->ops->say(cmd->client, conn,
956 "Usage: /MOTD [<server>]");
961 /* Send TOPIC command to the server */
963 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
964 1, conn->remote_host,
965 strlen(conn->remote_host));
967 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
970 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
971 0, NULL, NULL, buffer->data, buffer->len, TRUE);
972 silc_buffer_free(buffer);
974 /* Notify application */
978 silc_client_command_free(cmd);
981 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
982 modes as client cannot set itself server/router operator privileges. */
984 SILC_CLIENT_CMD_FUNC(umode)
986 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
987 SilcClientConnection conn = cmd->conn;
988 SilcBuffer buffer, idp;
989 unsigned char *cp, modebuf[4];
990 unsigned int mode, add, len;
994 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1000 cmd->client->ops->say(cmd->client, conn,
1001 "Usage: /UMODE +|-<modes>");
1006 mode = conn->local_entry->mode;
1008 /* Are we adding or removing mode */
1009 if (cmd->argv[1][0] == '-')
1015 cp = cmd->argv[1] + 1;
1017 for (i = 0; i < len; i++) {
1022 mode |= SILC_UMODE_SERVER_OPERATOR;
1023 mode |= SILC_UMODE_ROUTER_OPERATOR;
1025 mode = SILC_UMODE_NONE;
1030 mode |= SILC_UMODE_SERVER_OPERATOR;
1032 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1036 mode |= SILC_UMODE_ROUTER_OPERATOR;
1038 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1047 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1048 SILC_PUT32_MSB(mode, modebuf);
1050 /* Send the command packet. We support sending only one mode at once
1051 that requires an argument. */
1053 silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
1054 1, idp->data, idp->len,
1055 2, modebuf, sizeof(modebuf));
1056 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1057 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1058 silc_buffer_free(buffer);
1059 silc_buffer_free(idp);
1061 /* Notify application */
1065 silc_client_command_free(cmd);
1068 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1069 can be set several at once. Those modes that require argument must be set
1070 separately (unless set with modes that does not require arguments). */
1072 SILC_CLIENT_CMD_FUNC(cmode)
1074 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1075 SilcClientConnection conn = cmd->conn;
1076 SilcChannelEntry channel;
1077 SilcBuffer buffer, chidp;
1078 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1079 unsigned int mode, add, type, len, arg_len = 0;
1083 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1088 if (cmd->argc < 3) {
1089 cmd->client->ops->say(cmd->client, conn,
1090 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1095 if (cmd->argv[1][0] == '*') {
1096 if (!conn->current_channel) {
1097 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1102 channel = conn->current_channel;
1104 name = cmd->argv[1];
1106 channel = silc_client_get_channel(cmd->client, conn, name);
1108 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1114 mode = channel->mode;
1116 /* Are we adding or removing mode */
1117 if (cmd->argv[2][0] == '-')
1122 /* Argument type to be sent to server */
1126 cp = cmd->argv[2] + 1;
1128 for (i = 0; i < len; i++) {
1132 mode |= SILC_CHANNEL_MODE_PRIVATE;
1134 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1138 mode |= SILC_CHANNEL_MODE_SECRET;
1140 mode &= ~SILC_CHANNEL_MODE_SECRET;
1144 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1146 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1150 mode |= SILC_CHANNEL_MODE_INVITE;
1152 mode &= ~SILC_CHANNEL_MODE_INVITE;
1156 mode |= SILC_CHANNEL_MODE_TOPIC;
1158 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1163 mode |= SILC_CHANNEL_MODE_ULIMIT;
1165 ll = atoi(cmd->argv[3]);
1166 SILC_PUT32_MSB(ll, tmp);
1170 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1175 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1178 arg_len = cmd->argv_lens[3];
1180 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1185 mode |= SILC_CHANNEL_MODE_BAN;
1188 arg_len = cmd->argv_lens[3];
1190 mode &= ~SILC_CHANNEL_MODE_BAN;
1195 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
1198 arg_len = cmd->argv_lens[3];
1200 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
1205 mode |= SILC_CHANNEL_MODE_CIPHER;
1208 arg_len = cmd->argv_lens[3];
1210 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1220 if (type && cmd->argc < 3) {
1225 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1226 SILC_PUT32_MSB(mode, modebuf);
1228 /* Send the command packet. We support sending only one mode at once
1229 that requires an argument. */
1232 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1233 1, chidp->data, chidp->len,
1234 2, modebuf, sizeof(modebuf),
1235 type, arg, arg_len);
1238 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1239 1, chidp->data, chidp->len,
1240 2, modebuf, sizeof(modebuf));
1243 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1244 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1245 silc_buffer_free(buffer);
1246 silc_buffer_free(chidp);
1248 /* Notify application */
1252 silc_client_command_free(cmd);
1255 /* CUMODE command. Changes client's mode on a channel. */
1257 SILC_CLIENT_CMD_FUNC(cumode)
1259 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1260 SilcClientConnection conn = cmd->conn;
1261 SilcChannelEntry channel;
1262 SilcChannelUser chu;
1263 SilcClientEntry client_entry;
1264 SilcBuffer buffer, clidp, chidp;
1265 unsigned char *name, *cp, modebuf[4];
1266 unsigned int mode = 0, add, len;
1267 char *nickname = NULL, *server = NULL;
1268 unsigned int num = 0;
1272 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1277 if (cmd->argc < 4) {
1278 cmd->client->ops->say(cmd->client, conn,
1279 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1284 if (cmd->argv[1][0] == '*') {
1285 if (!conn->current_channel) {
1286 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1291 channel = conn->current_channel;
1293 name = cmd->argv[1];
1295 channel = silc_client_get_channel(cmd->client, conn, name);
1297 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1303 /* Parse the typed nickname. */
1304 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1305 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1310 /* Find client entry */
1311 client_entry = silc_idlist_get_client(cmd->client, conn,
1312 nickname, server, num, TRUE);
1313 if (!client_entry) {
1314 /* Client entry not found, it was requested thus mark this to be
1316 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1318 silc_client_command_destructor,
1319 silc_client_command_cumode,
1320 silc_client_command_dup(cmd));
1325 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1326 if (chu->client == client_entry) {
1332 /* Are we adding or removing mode */
1333 if (cmd->argv[2][0] == '-')
1339 cp = cmd->argv[2] + 1;
1341 for (i = 0; i < len; i++) {
1345 mode |= SILC_CHANNEL_UMODE_CHANFO;
1346 mode |= SILC_CHANNEL_UMODE_CHANOP;
1348 mode = SILC_CHANNEL_UMODE_NONE;
1353 mode |= SILC_CHANNEL_UMODE_CHANFO;
1355 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1359 mode |= SILC_CHANNEL_UMODE_CHANOP;
1361 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1370 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1371 SILC_PUT32_MSB(mode, modebuf);
1372 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1374 /* Send the command packet. We support sending only one mode at once
1375 that requires an argument. */
1376 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1377 1, chidp->data, chidp->len,
1379 3, clidp->data, clidp->len);
1381 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1382 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1383 silc_buffer_free(buffer);
1384 silc_buffer_free(chidp);
1385 silc_buffer_free(clidp);
1387 /* Notify application */
1392 silc_free(nickname);
1395 silc_client_command_free(cmd);
1398 /* KICK command. Kicks a client out of channel. */
1400 SILC_CLIENT_CMD_FUNC(kick)
1402 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1403 SilcClientConnection conn = cmd->conn;
1404 SilcIDCacheEntry id_cache = NULL;
1405 SilcChannelEntry channel;
1406 SilcBuffer buffer, idp, idp2;
1407 SilcClientEntry target;
1409 unsigned int num = 0;
1410 char *nickname = NULL, *server = NULL;
1413 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1418 if (cmd->argc < 3) {
1419 cmd->client->ops->say(cmd->client, conn,
1420 "Usage: /KICK <channel> <nickname> [<comment>]");
1425 if (cmd->argv[1][0] == '*') {
1426 if (!conn->current_channel) {
1427 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1431 name = conn->current_channel->channel_name;
1433 name = cmd->argv[1];
1436 if (!conn->current_channel) {
1437 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1442 /* Get the Channel ID of the channel */
1443 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1444 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1449 channel = (SilcChannelEntry)id_cache->context;
1451 /* Parse the typed nickname. */
1452 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1453 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1458 /* Get the target client */
1459 target = silc_idlist_get_client(cmd->client, conn, nickname,
1460 server, num, FALSE);
1462 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1468 /* Send KICK command to the server */
1469 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1470 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1472 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1473 1, idp->data, idp->len,
1474 2, idp2->data, idp2->len);
1476 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1477 1, idp->data, idp->len,
1478 2, idp2->data, idp2->len,
1480 strlen(cmd->argv[3]));
1481 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1482 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1483 silc_buffer_free(buffer);
1484 silc_buffer_free(idp);
1485 silc_buffer_free(idp2);
1487 /* Notify application */
1492 silc_free(nickname);
1495 silc_client_command_free(cmd);
1498 /* OPER command. Used to obtain server operator privileges. */
1500 SILC_CLIENT_CMD_FUNC(oper)
1502 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1503 SilcClientConnection conn = cmd->conn;
1505 unsigned char *auth_data;
1509 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1514 if (cmd->argc < 2) {
1515 cmd->client->ops->say(cmd->client, conn,
1516 "Usage: /OPER <username> [<public key>]");
1521 if (cmd->argc == 3) {
1522 /* XXX Get public key */
1527 /* Get passphrase */
1529 auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1535 /* Encode the authentication payload */
1536 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1537 auth_data, strlen(auth_data));
1540 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1542 strlen(cmd->argv[1]),
1543 2, auth->data, auth->len);
1544 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1545 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1547 silc_buffer_free(buffer);
1548 silc_buffer_free(auth);
1549 memset(auth_data, 0, strlen(auth_data));
1550 silc_free(auth_data);
1552 /* Notify application */
1556 silc_client_command_free(cmd);
1559 /* SILCOPER command. Used to obtain router operator privileges. */
1561 SILC_CLIENT_CMD_FUNC(silcoper)
1563 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1564 SilcClientConnection conn = cmd->conn;
1566 unsigned char *auth_data;
1570 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1575 if (cmd->argc < 2) {
1576 cmd->client->ops->say(cmd->client, conn,
1577 "Usage: /SILCOPER <username> [<public key>]");
1582 if (cmd->argc == 3) {
1583 /* XXX Get public key */
1588 /* Get passphrase */
1590 auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1596 /* Encode the authentication payload */
1597 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1598 auth_data, strlen(auth_data));
1601 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1603 strlen(cmd->argv[1]),
1604 2, auth->data, auth->len);
1605 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1606 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1608 silc_buffer_free(buffer);
1609 silc_buffer_free(auth);
1610 memset(auth_data, 0, strlen(auth_data));
1611 silc_free(auth_data);
1613 /* Notify application */
1617 silc_client_command_free(cmd);
1620 /* CONNECT command. Connects the server to another server. */
1622 SILC_CLIENT_CMD_FUNC(connect)
1624 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1625 SilcClientConnection conn = cmd->conn;
1627 unsigned char port[4];
1631 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1636 if (cmd->argc < 2) {
1637 cmd->client->ops->say(cmd->client, conn,
1638 "Usage: /CONNECT <server> [<port>]");
1643 if (cmd->argc == 3) {
1644 tmp = atoi(cmd->argv[2]);
1645 SILC_PUT32_MSB(tmp, port);
1649 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1651 strlen(cmd->argv[1]),
1654 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1656 strlen(cmd->argv[1]));
1657 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1658 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1659 silc_buffer_free(buffer);
1661 /* Notify application */
1665 silc_client_command_free(cmd);
1668 SILC_CLIENT_CMD_FUNC(restart)
1672 /* CLOSE command. Close server connection to the remote server */
1674 SILC_CLIENT_CMD_FUNC(close)
1676 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1677 SilcClientConnection conn = cmd->conn;
1679 unsigned char port[4];
1683 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1688 if (cmd->argc < 2) {
1689 cmd->client->ops->say(cmd->client, conn,
1690 "Usage: /CLOSE <server> [<port>]");
1695 if (cmd->argc == 3) {
1696 tmp = atoi(cmd->argv[2]);
1697 SILC_PUT32_MSB(tmp, port);
1701 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1703 strlen(cmd->argv[1]),
1706 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1708 strlen(cmd->argv[1]));
1709 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1710 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1711 silc_buffer_free(buffer);
1713 /* Notify application */
1717 silc_client_command_free(cmd);
1720 /* SHUTDOWN command. Shutdowns the server. */
1722 SILC_CLIENT_CMD_FUNC(shutdown)
1724 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1727 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1732 /* Send the command */
1733 silc_client_send_command(cmd->client, cmd->conn,
1734 SILC_COMMAND_SHUTDOWN, 0, 0);
1736 /* Notify application */
1740 silc_client_command_free(cmd);
1743 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1745 SILC_CLIENT_CMD_FUNC(leave)
1747 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1748 SilcClientConnection conn = cmd->conn;
1749 SilcIDCacheEntry id_cache = NULL;
1750 SilcChannelEntry channel;
1751 SilcBuffer buffer, idp;
1755 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1760 if (cmd->argc != 2) {
1761 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1766 if (cmd->argv[1][0] == '*') {
1767 if (!conn->current_channel) {
1768 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1772 name = conn->current_channel->channel_name;
1774 name = cmd->argv[1];
1777 if (!conn->current_channel) {
1778 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1783 /* Get the Channel ID of the channel */
1784 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1785 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1790 channel = (SilcChannelEntry)id_cache->context;
1792 /* Send LEAVE command to the server */
1793 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1794 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1795 1, idp->data, idp->len);
1796 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1797 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1798 silc_buffer_free(buffer);
1799 silc_buffer_free(idp);
1801 /* We won't talk anymore on this channel */
1802 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1804 conn->current_channel = NULL;
1806 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1807 silc_free(channel->channel_name);
1808 silc_free(channel->id);
1809 silc_free(channel->key);
1810 silc_cipher_free(channel->channel_key);
1813 /* Notify application */
1817 silc_client_command_free(cmd);
1820 /* Command USERS. Requests the USERS of the clients joined on requested
1823 SILC_CLIENT_CMD_FUNC(users)
1825 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1826 SilcClientConnection conn = cmd->conn;
1827 SilcIDCacheEntry id_cache = NULL;
1828 SilcChannelEntry channel;
1829 SilcBuffer buffer, idp;
1830 char *name, *line = NULL;
1831 unsigned int line_len = 0;
1834 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1839 if (cmd->argc != 2) {
1840 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1845 if (cmd->argv[1][0] == '*') {
1846 if (!conn->current_channel) {
1847 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1851 name = conn->current_channel->channel_name;
1853 name = cmd->argv[1];
1856 if (!conn->current_channel) {
1857 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1862 /* Get the Channel ID of the channel */
1863 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1864 /* XXX should resolve the channel ID; LIST command */
1865 cmd->client->ops->say(cmd->client, conn,
1866 "You are not on that channel", name);
1871 channel = (SilcChannelEntry)id_cache->context;
1873 if (!cmd->pending) {
1874 /* Send USERS command to the server */
1875 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1876 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
1877 ++conn->cmd_ident, 1,
1878 1, idp->data, idp->len);
1879 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1880 NULL, 0, NULL, NULL, buffer->data,
1882 silc_buffer_free(buffer);
1883 silc_buffer_free(idp);
1885 /* Register pending callback which will recall this command callback with
1886 same context and reprocesses the command. When reprocessing we actually
1887 display the information on the screen. */
1888 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
1889 silc_client_command_destructor,
1890 silc_client_command_users,
1891 silc_client_command_dup(cmd));
1892 cmd->pending = TRUE;
1897 /* Pending command. Now we've resolved the information from server and
1898 we are ready to display the information on screen. */
1900 SilcChannelUser chu;
1902 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1903 channel->channel_name);
1905 line = silc_calloc(4096, sizeof(*line));
1907 silc_list_start(channel->clients);
1908 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1909 SilcClientEntry e = chu->client;
1910 char *m, tmp[80], len1;
1912 memset(line, 0, sizeof(line_len));
1914 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1916 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1917 line = silc_calloc(line_len, sizeof(*line));
1920 memset(tmp, 0, sizeof(tmp));
1921 m = silc_client_chumode_char(chu->mode);
1923 strncat(line, " ", 1);
1924 strncat(line, e->nickname, strlen(e->nickname));
1925 strncat(line, e->server ? "@" : "", 1);
1929 len1 = strlen(e->server);
1930 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1932 len1 = strlen(line);
1934 memset(&line[29], 0, len1 - 29);
1936 for (i = 0; i < 30 - len1 - 1; i++)
1940 strncat(line, " H", 3);
1941 strcat(tmp, m ? m : "");
1942 strncat(line, tmp, strlen(tmp));
1944 if (strlen(tmp) < 5)
1945 for (i = 0; i < 5 - strlen(tmp); i++)
1948 strcat(line, e->username ? e->username : "");
1950 cmd->client->ops->say(cmd->client, conn, "%s", line);
1960 /* Notify application */
1964 silc_client_command_free(cmd);