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.
22 #include "clientlibincludes.h"
24 /* Client command list. */
25 SilcClientCommand silc_command_list[] =
27 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
28 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
29 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
30 SILC_CF_LAG | SILC_CF_REG, 3),
31 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
32 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
33 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
34 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
35 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
36 SILC_CLIENT_CMD(kill, KILL, "KILL",
37 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
38 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
39 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
40 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
41 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
42 SILC_CLIENT_CMD(oper, OPER, "OPER",
43 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
44 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 4),
45 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
46 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
48 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
49 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
50 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
51 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
52 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
53 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54 SILC_CLIENT_CMD(die, DIE, "DIE",
55 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
56 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
57 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
58 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
59 SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
61 { NULL, 0, NULL, 0, 0 },
64 #define SILC_NOT_CONNECTED(x, c) \
65 x->ops->say((x), (c), \
66 "You are not connected to a server, use /SERVER to connect");
68 /* Command operation that is called at the end of all commands.
70 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
71 cmd, TRUE, cmd->command->cmd)
73 /* Error to application. Usage: COMMAND_ERROR; */
74 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
75 cmd, FALSE, cmd->command->cmd)
77 /* Generic function to send any command. The arguments must be sent already
78 encoded into correct form in correct order. */
80 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
81 SilcCommand command, unsigned short ident,
82 unsigned int argc, ...)
89 packet = silc_command_payload_encode_vap(command, ident, argc, ap);
90 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
91 NULL, 0, NULL, NULL, packet->data,
93 silc_buffer_free(packet);
96 /* Finds and returns a pointer to the command list. Return NULL if the
97 command is not found. */
99 SilcClientCommand *silc_client_command_find(const char *name)
101 SilcClientCommand *cmd;
103 for (cmd = silc_command_list; cmd->name; cmd++) {
104 if (!strcmp(cmd->name, name))
111 /* Add new pending command to be executed when reply to a command has been
112 received. The `reply_cmd' is the command that will call the `callback'
113 with `context' when reply has been received. If `ident is non-zero
114 the `callback' will be executed when received reply with command
115 identifier `ident'. */
117 void silc_client_command_pending(SilcClientConnection conn,
118 SilcCommand reply_cmd,
119 unsigned short ident,
120 SilcClientPendingDestructor destructor,
121 SilcCommandCb callback,
124 SilcClientCommandPending *reply;
126 reply = silc_calloc(1, sizeof(*reply));
127 reply->reply_cmd = reply_cmd;
128 reply->ident = ident;
129 reply->context = context;
130 reply->callback = callback;
131 reply->destructor = destructor;
132 silc_dlist_add(conn->pending_commands, reply);
135 /* Deletes pending command by reply command type. */
137 void silc_client_command_pending_del(SilcClientConnection conn,
138 SilcCommand reply_cmd,
139 unsigned short ident)
141 SilcClientCommandPending *r;
143 silc_dlist_start(conn->pending_commands);
144 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
145 if (r->reply_cmd == reply_cmd && r->ident == ident) {
146 silc_dlist_del(conn->pending_commands, r);
152 /* Checks for pending commands and marks callbacks to be called from
153 the command reply function. Returns TRUE if there were pending command. */
155 int silc_client_command_pending_check(SilcClientConnection conn,
156 SilcClientCommandReplyContext ctx,
158 unsigned short ident)
160 SilcClientCommandPending *r;
162 silc_dlist_start(conn->pending_commands);
163 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
164 if (r->reply_cmd == command && r->ident == ident) {
165 ctx->context = r->context;
166 ctx->callback = r->callback;
167 ctx->destructor = r->destructor;
176 /* Allocate Command Context */
178 SilcClientCommandContext silc_client_command_alloc()
180 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
185 /* Free command context and its internals */
187 void silc_client_command_free(SilcClientCommandContext ctx)
190 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
192 if (ctx->users < 1) {
195 for (i = 0; i < ctx->argc; i++)
196 silc_free(ctx->argv[i]);
201 /* Duplicate Command Context by adding reference counter. The context won't
202 be free'd untill it hits zero. */
204 SilcClientCommandContext
205 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 /* Command WHOIS. This command is used to query information about
223 SILC_CLIENT_CMD_FUNC(whois)
225 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
226 SilcClientConnection conn = cmd->conn;
230 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
235 if (cmd->argc < 2 || cmd->argc > 3) {
236 cmd->client->ops->say(cmd->client, conn,
237 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
242 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
243 cmd->argc - 1, ++cmd->argv,
244 ++cmd->argv_lens, ++cmd->argv_types,
246 silc_client_packet_send(cmd->client, cmd->conn->sock,
247 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
248 buffer->data, buffer->len, TRUE);
249 silc_buffer_free(buffer);
254 /* Notify application */
258 silc_client_command_free(cmd);
261 SILC_CLIENT_CMD_FUNC(whowas)
265 /* Command IDENTIFY. This command is used to query information about
266 specific user, especially ID's. */
268 SILC_CLIENT_CMD_FUNC(identify)
270 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
271 SilcClientConnection conn = cmd->conn;
275 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
280 if (cmd->argc < 2 || cmd->argc > 3) {
281 cmd->client->ops->say(cmd->client, conn,
282 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
287 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
288 cmd->argc - 1, ++cmd->argv,
289 ++cmd->argv_lens, ++cmd->argv_types,
291 silc_client_packet_send(cmd->client, cmd->conn->sock,
292 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
293 buffer->data, buffer->len, TRUE);
294 silc_buffer_free(buffer);
299 /* Notify application */
303 silc_client_command_free(cmd);
306 /* Command NICK. Shows current nickname/sets new nickname on current
309 SILC_CLIENT_CMD_FUNC(nick)
311 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
312 SilcClientConnection conn = cmd->conn;
316 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
321 if (!strcmp(conn->nickname, cmd->argv[1]))
324 /* Show current nickname */
327 cmd->client->ops->say(cmd->client, conn,
328 "Your nickname is %s on server %s",
329 conn->nickname, conn->remote_host);
331 cmd->client->ops->say(cmd->client, conn,
332 "Your nickname is %s", conn->nickname);
335 /* XXX Notify application */
340 /* Set new nickname */
341 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
342 cmd->argc - 1, ++cmd->argv,
343 ++cmd->argv_lens, ++cmd->argv_types,
345 silc_client_packet_send(cmd->client, cmd->conn->sock,
346 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
347 buffer->data, buffer->len, TRUE);
348 silc_buffer_free(buffer);
353 silc_free(conn->nickname);
354 conn->nickname = strdup(cmd->argv[1]);
356 /* Notify application */
360 silc_client_command_free(cmd);
363 SILC_CLIENT_CMD_FUNC(list)
367 /* Command TOPIC. Sets/shows topic on a channel. */
369 SILC_CLIENT_CMD_FUNC(topic)
371 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
372 SilcClientConnection conn = cmd->conn;
373 SilcIDCacheEntry id_cache = NULL;
374 SilcChannelEntry channel;
375 SilcBuffer buffer, idp;
379 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
384 if (cmd->argc < 2 || cmd->argc > 3) {
385 cmd->client->ops->say(cmd->client, conn,
386 "Usage: /TOPIC <channel> [<topic>]");
391 if (cmd->argv[1][0] == '*') {
392 if (!conn->current_channel) {
393 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
397 name = conn->current_channel->channel_name;
402 if (!conn->current_channel) {
403 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
408 /* Get the Channel ID of the channel */
409 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
410 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
415 channel = (SilcChannelEntry)id_cache->context;
417 /* Send TOPIC command to the server */
418 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
420 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
421 1, idp->data, idp->len,
423 strlen(cmd->argv[2]));
425 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
426 1, idp->data, idp->len,
428 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
429 0, NULL, NULL, buffer->data, buffer->len, TRUE);
430 silc_buffer_free(buffer);
431 silc_buffer_free(idp);
433 /* Notify application */
437 silc_client_command_free(cmd);
440 /* Command INVITE. Invites specific client to join a channel. */
442 SILC_CLIENT_CMD_FUNC(invite)
444 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
445 SilcClient client = cmd->client;
446 SilcClientConnection conn = cmd->conn;
447 SilcClientEntry client_entry;
448 SilcChannelEntry channel_entry;
449 SilcBuffer buffer, clidp, chidp;
450 unsigned int num = 0;
451 char *nickname = NULL, *server = NULL;
454 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
459 if (cmd->argc != 3) {
460 cmd->client->ops->say(cmd->client, conn,
461 "Usage: /INVITE <nickname>[@<server>] <channel>");
466 /* Parse the typed nickname. */
467 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
468 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
473 /* Find client entry */
474 client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
481 /* Client entry not found, it was requested thus mark this to be
483 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
484 silc_client_command_destructor,
485 silc_client_command_invite,
486 silc_client_command_dup(cmd));
491 /* Find channel entry */
492 channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
493 if (!channel_entry) {
494 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
500 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
501 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
502 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
503 1, clidp->data, clidp->len,
504 2, chidp->data, chidp->len);
505 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
506 0, NULL, NULL, buffer->data, buffer->len, TRUE);
507 silc_buffer_free(buffer);
508 silc_buffer_free(clidp);
509 silc_buffer_free(chidp);
511 cmd->client->ops->say(cmd->client, conn,
512 "Inviting %s to channel %s", cmd->argv[1],
515 /* Notify application */
519 silc_client_command_free(cmd);
522 /* Command QUIT. Closes connection with current server. */
524 SILC_CLIENT_CMD_FUNC(quit)
526 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
530 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
535 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
536 ++cmd->argv, ++cmd->argv_lens,
537 ++cmd->argv_types, 0);
538 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
540 buffer->data, buffer->len, TRUE);
541 silc_buffer_free(buffer);
546 /* Close connection */
547 cmd->client->ops->disconnect(cmd->client, cmd->conn);
548 silc_client_close_connection(cmd->client, cmd->conn->sock);
550 /* Notify application */
554 silc_client_command_free(cmd);
557 SILC_CLIENT_CMD_FUNC(kill)
561 /* Command INFO. Request information about specific server. If specific
562 server is not provided the current server is used. */
564 SILC_CLIENT_CMD_FUNC(info)
566 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
567 SilcClientConnection conn = cmd->conn;
572 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
578 name = strdup(conn->remote_host);
580 name = strdup(cmd->argv[1]);
582 /* Send the command */
583 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
584 1, name, strlen(name));
585 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
586 0, NULL, NULL, buffer->data, buffer->len, TRUE);
587 silc_buffer_free(buffer);
589 /* Notify application */
593 silc_client_command_free(cmd);
596 SILC_CLIENT_CMD_FUNC(connect)
600 /* Command PING. Sends ping to server. This is used to test the
601 communication channel. */
603 SILC_CLIENT_CMD_FUNC(ping)
605 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
606 SilcClientConnection conn = cmd->conn;
613 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
618 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
619 name = strdup(conn->remote_host);
621 /* Send the command */
622 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
623 1, conn->remote_id_data,
625 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
626 0, NULL, NULL, buffer->data, buffer->len, TRUE);
627 silc_buffer_free(buffer);
629 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
632 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
637 /* Start counting time */
638 for (i = 0; i < conn->ping_count; i++) {
639 if (conn->ping[i].dest_id == NULL) {
640 conn->ping[i].start_time = time(NULL);
641 conn->ping[i].dest_id = id;
642 conn->ping[i].dest_name = name;
647 if (i >= conn->ping_count) {
648 i = conn->ping_count;
649 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
650 conn->ping[i].start_time = time(NULL);
651 conn->ping[i].dest_id = id;
652 conn->ping[i].dest_name = name;
656 /* Notify application */
660 silc_client_command_free(cmd);
663 SILC_CLIENT_CMD_FUNC(oper)
667 SILC_CLIENT_CMD_FUNC(trace)
671 SILC_CLIENT_CMD_FUNC(notice)
675 /* Command JOIN. Joins to a channel. */
677 SILC_CLIENT_CMD_FUNC(join)
679 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
680 SilcClientConnection conn = cmd->conn;
681 SilcIDCacheEntry id_cache = NULL;
682 SilcBuffer buffer, idp;
685 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
691 /* Show channels currently joined to */
696 /* See if we have joined to the requested channel already */
697 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
699 cmd->client->ops->say(cmd->client, conn,
700 "You are talking to channel %s", cmd->argv[1]);
701 conn->current_channel = (SilcChannelEntry)id_cache->context;
703 cmd->client->screen->bottom_line->channel = cmd->argv[1];
704 silc_screen_print_bottom_line(cmd->client->screen, 0);
709 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
711 /* Send JOIN command to the server */
714 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
715 1, cmd->argv[1], cmd->argv_lens[1],
716 2, idp->data, idp->len);
717 else if (cmd->argc == 3)
720 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
721 1, cmd->argv[1], cmd->argv_lens[1],
722 2, idp->data, idp->len,
723 3, cmd->argv[2], cmd->argv_lens[2]);
726 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
727 1, cmd->argv[1], cmd->argv_lens[1],
728 2, idp->data, idp->len,
729 3, cmd->argv[2], cmd->argv_lens[2],
730 4, cmd->argv[3], cmd->argv_lens[3]);
732 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
733 0, NULL, NULL, buffer->data, buffer->len, TRUE);
734 silc_buffer_free(buffer);
735 silc_buffer_free(idp);
737 /* Notify application */
741 silc_client_command_free(cmd);
744 /* MOTD command. Requests motd from server. */
746 SILC_CLIENT_CMD_FUNC(motd)
748 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
749 SilcClientConnection conn = cmd->conn;
753 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
758 if (cmd->argc < 1 || cmd->argc > 1) {
759 cmd->client->ops->say(cmd->client, conn,
765 /* Send TOPIC command to the server */
766 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
767 2, conn->remote_host,
768 strlen(conn->remote_host));
769 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
770 0, NULL, NULL, buffer->data, buffer->len, TRUE);
771 silc_buffer_free(buffer);
773 /* Notify application */
777 silc_client_command_free(cmd);
780 /* UMODE. Set user mode in SILC. */
782 SILC_CLIENT_CMD_FUNC(umode)
787 /* CMODE command. Sets channel mode. Modes that does not require any arguments
788 can be set several at once. Those modes that require argument must be set
789 separately (unless set with modes that does not require arguments). */
791 SILC_CLIENT_CMD_FUNC(cmode)
793 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
794 SilcClientConnection conn = cmd->conn;
795 SilcChannelEntry channel;
796 SilcBuffer buffer, chidp;
797 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
798 unsigned int mode, add, type, len, arg_len = 0;
802 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
808 cmd->client->ops->say(cmd->client, conn,
809 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
814 if (cmd->argv[1][0] == '*') {
815 if (!conn->current_channel) {
816 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
821 channel = conn->current_channel;
825 channel = silc_idlist_get_channel(cmd->client, conn, name);
827 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
833 mode = channel->mode;
835 /* Are we adding or removing mode */
836 if (cmd->argv[2][0] == '-')
841 /* Argument type to be sent to server */
845 cp = cmd->argv[2] + 1;
847 for (i = 0; i < len; i++) {
851 mode |= SILC_CHANNEL_MODE_PRIVATE;
853 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
857 mode |= SILC_CHANNEL_MODE_SECRET;
859 mode &= ~SILC_CHANNEL_MODE_SECRET;
863 mode |= SILC_CHANNEL_MODE_PRIVKEY;
865 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
869 mode |= SILC_CHANNEL_MODE_INVITE;
871 mode &= ~SILC_CHANNEL_MODE_INVITE;
875 mode |= SILC_CHANNEL_MODE_TOPIC;
877 mode &= ~SILC_CHANNEL_MODE_TOPIC;
882 mode |= SILC_CHANNEL_MODE_ULIMIT;
884 ll = atoi(cmd->argv[3]);
885 SILC_PUT32_MSB(ll, tmp);
889 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
894 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
897 arg_len = cmd->argv_lens[3];
899 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
904 mode |= SILC_CHANNEL_MODE_BAN;
907 arg_len = cmd->argv_lens[3];
909 mode &= ~SILC_CHANNEL_MODE_BAN;
914 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
917 arg_len = cmd->argv_lens[3];
919 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
924 mode |= SILC_CHANNEL_MODE_CIPHER;
927 arg_len = cmd->argv_lens[3];
929 mode &= ~SILC_CHANNEL_MODE_CIPHER;
939 if (type && cmd->argc < 3) {
944 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
945 SILC_PUT32_MSB(mode, modebuf);
947 /* Send the command packet. We support sending only one mode at once
948 that requires an argument. */
951 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
952 1, chidp->data, chidp->len,
953 2, modebuf, sizeof(modebuf),
957 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
958 1, chidp->data, chidp->len,
959 2, modebuf, sizeof(modebuf));
962 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
963 0, NULL, NULL, buffer->data, buffer->len, TRUE);
964 silc_buffer_free(buffer);
965 silc_buffer_free(chidp);
967 /* Notify application */
971 silc_client_command_free(cmd);
974 /* CUMODE command. Changes client's mode on a channel. */
976 SILC_CLIENT_CMD_FUNC(cumode)
978 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
979 SilcClientConnection conn = cmd->conn;
980 SilcChannelEntry channel;
982 SilcClientEntry client_entry;
983 SilcBuffer buffer, clidp, chidp;
984 unsigned char *name, *cp, modebuf[4];
985 unsigned int mode = 0, add, len;
986 char *nickname = NULL, *server = NULL;
987 unsigned int num = 0;
991 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
997 cmd->client->ops->say(cmd->client, conn,
998 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1003 if (cmd->argv[1][0] == '*') {
1004 if (!conn->current_channel) {
1005 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1010 channel = conn->current_channel;
1012 name = cmd->argv[1];
1014 channel = silc_idlist_get_channel(cmd->client, conn, name);
1016 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1022 /* Parse the typed nickname. */
1023 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1024 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1029 /* Find client entry */
1030 client_entry = silc_idlist_get_client(cmd->client, conn,
1031 nickname, server, num);
1032 if (!client_entry) {
1033 /* Client entry not found, it was requested thus mark this to be
1035 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1036 silc_client_command_destructor,
1037 silc_client_command_cumode,
1038 silc_client_command_dup(cmd));
1043 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1044 if (chu->client == client_entry) {
1050 /* Are we adding or removing mode */
1051 if (cmd->argv[2][0] == '-')
1057 cp = cmd->argv[2] + 1;
1059 for (i = 0; i < len; i++) {
1063 mode |= SILC_CHANNEL_UMODE_CHANFO;
1064 mode |= SILC_CHANNEL_UMODE_CHANOP;
1066 mode = SILC_CHANNEL_UMODE_NONE;
1071 mode |= SILC_CHANNEL_UMODE_CHANFO;
1073 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1077 mode |= SILC_CHANNEL_UMODE_CHANOP;
1079 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1088 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1089 SILC_PUT32_MSB(mode, modebuf);
1090 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1092 /* Send the command packet. We support sending only one mode at once
1093 that requires an argument. */
1094 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1095 1, chidp->data, chidp->len,
1097 3, clidp->data, clidp->len);
1099 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1100 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1101 silc_buffer_free(buffer);
1102 silc_buffer_free(chidp);
1103 silc_buffer_free(clidp);
1105 /* Notify application */
1109 silc_client_command_free(cmd);
1112 /* KICK command. Kicks a client out of channel. */
1114 SILC_CLIENT_CMD_FUNC(kick)
1116 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1117 SilcClientConnection conn = cmd->conn;
1121 SILC_CLIENT_CMD_FUNC(restart)
1125 SILC_CLIENT_CMD_FUNC(close)
1129 SILC_CLIENT_CMD_FUNC(die)
1133 SILC_CLIENT_CMD_FUNC(silcoper)
1137 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1139 SILC_CLIENT_CMD_FUNC(leave)
1141 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1142 SilcClientConnection conn = cmd->conn;
1143 SilcIDCacheEntry id_cache = NULL;
1144 SilcChannelEntry channel;
1145 SilcBuffer buffer, idp;
1149 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1154 if (cmd->argc != 2) {
1155 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1160 if (cmd->argv[1][0] == '*') {
1161 if (!conn->current_channel) {
1162 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1166 name = conn->current_channel->channel_name;
1168 name = cmd->argv[1];
1171 if (!conn->current_channel) {
1172 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1177 /* Get the Channel ID of the channel */
1178 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1179 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1184 channel = (SilcChannelEntry)id_cache->context;
1186 /* Send LEAVE command to the server */
1187 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1188 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1189 1, idp->data, idp->len);
1190 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1191 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1192 silc_buffer_free(buffer);
1193 silc_buffer_free(idp);
1195 /* We won't talk anymore on this channel */
1196 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1198 conn->current_channel = NULL;
1200 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1201 silc_free(channel->channel_name);
1202 silc_free(channel->id);
1203 silc_free(channel->key);
1204 silc_cipher_free(channel->channel_key);
1207 /* Notify application */
1211 silc_client_command_free(cmd);
1214 /* Command USERS. Requests the USERS of the clients joined on requested
1217 SILC_CLIENT_CMD_FUNC(users)
1219 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1220 SilcClientConnection conn = cmd->conn;
1221 SilcIDCacheEntry id_cache = NULL;
1222 SilcChannelEntry channel;
1223 SilcBuffer buffer, idp;
1224 char *name, *line = NULL;
1225 unsigned int line_len = 0;
1228 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1233 if (cmd->argc != 2) {
1234 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1239 if (cmd->argv[1][0] == '*') {
1240 if (!conn->current_channel) {
1241 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1245 name = conn->current_channel->channel_name;
1247 name = cmd->argv[1];
1250 if (!conn->current_channel) {
1251 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1256 /* Get the Channel ID of the channel */
1257 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1258 /* XXX should resolve the channel ID; LIST command */
1259 cmd->client->ops->say(cmd->client, conn,
1260 "You are not on that channel", name);
1265 channel = (SilcChannelEntry)id_cache->context;
1267 if (!cmd->pending) {
1268 /* Send USERS command to the server */
1269 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1270 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1271 1, idp->data, idp->len);
1272 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1273 NULL, 0, NULL, NULL, buffer->data,
1275 silc_buffer_free(buffer);
1276 silc_buffer_free(idp);
1278 /* Register pending callback which will recall this command callback with
1279 same context and reprocesses the command. When reprocessing we actually
1280 display the information on the screen. */
1281 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1282 silc_client_command_destructor,
1283 silc_client_command_users,
1284 silc_client_command_dup(cmd));
1285 cmd->pending = TRUE;
1290 /* Pending command. Now we've resolved the information from server and
1291 we are ready to display the information on screen. */
1293 SilcChannelUser chu;
1295 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1296 channel->channel_name);
1298 line = silc_calloc(4096, sizeof(*line));
1300 silc_list_start(channel->clients);
1301 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1302 SilcClientEntry e = chu->client;
1303 char *m, tmp[80], len1;
1305 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1307 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1308 line = silc_calloc(line_len, sizeof(*line));
1311 memset(tmp, 0, sizeof(tmp));
1312 m = silc_client_chumode_char(chu->mode);
1314 strncat(line, " ", 1);
1315 strncat(line, e->nickname, strlen(e->nickname));
1316 strncat(line, e->server ? "@" : "", 1);
1320 len1 = strlen(e->server);
1321 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1323 len1 = strlen(line);
1325 memset(&line[29], 0, len1 - 29);
1327 for (i = 0; i < 30 - len1 - 1; i++)
1331 strncat(line, " H", 3);
1332 strcat(tmp, m ? m : "");
1333 strncat(line, tmp, strlen(tmp));
1335 if (strlen(tmp) < 5)
1336 for (i = 0; i < 5 - strlen(tmp); i++)
1339 strcat(line, e->username ? e->username : "");
1341 cmd->client->ops->say(cmd->client, conn, "%s", line);
1351 /* Notify application */
1355 silc_client_command_free(cmd);