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));
490 /* Find channel entry */
491 channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
492 if (!channel_entry) {
493 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
499 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
500 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
501 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
502 1, clidp->data, clidp->len,
503 2, chidp->data, chidp->len);
504 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
505 0, NULL, NULL, buffer->data, buffer->len, TRUE);
506 silc_buffer_free(buffer);
507 silc_buffer_free(clidp);
508 silc_buffer_free(chidp);
510 cmd->client->ops->say(cmd->client, conn,
511 "Inviting %s to channel %s", cmd->argv[1],
514 /* Notify application */
518 silc_client_command_free(cmd);
521 /* Command QUIT. Closes connection with current server. */
523 SILC_CLIENT_CMD_FUNC(quit)
525 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
529 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
534 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
535 ++cmd->argv, ++cmd->argv_lens,
536 ++cmd->argv_types, 0);
537 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
539 buffer->data, buffer->len, TRUE);
540 silc_buffer_free(buffer);
545 /* Close connection */
546 cmd->client->ops->disconnect(cmd->client, cmd->conn);
547 silc_client_close_connection(cmd->client, cmd->conn->sock);
549 /* Notify application */
553 silc_client_command_free(cmd);
556 SILC_CLIENT_CMD_FUNC(kill)
560 /* Command INFO. Request information about specific server. If specific
561 server is not provided the current server is used. */
563 SILC_CLIENT_CMD_FUNC(info)
565 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
566 SilcClientConnection conn = cmd->conn;
571 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
577 name = strdup(conn->remote_host);
579 name = strdup(cmd->argv[1]);
581 /* Send the command */
582 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
583 1, name, strlen(name));
584 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
585 0, NULL, NULL, buffer->data, buffer->len, TRUE);
586 silc_buffer_free(buffer);
588 /* Notify application */
592 silc_client_command_free(cmd);
595 SILC_CLIENT_CMD_FUNC(connect)
599 /* Command PING. Sends ping to server. This is used to test the
600 communication channel. */
602 SILC_CLIENT_CMD_FUNC(ping)
604 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
605 SilcClientConnection conn = cmd->conn;
612 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
617 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
618 name = strdup(conn->remote_host);
620 /* Send the command */
621 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
622 1, conn->remote_id_data,
624 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
625 0, NULL, NULL, buffer->data, buffer->len, TRUE);
626 silc_buffer_free(buffer);
628 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
631 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
636 /* Start counting time */
637 for (i = 0; i < conn->ping_count; i++) {
638 if (conn->ping[i].dest_id == NULL) {
639 conn->ping[i].start_time = time(NULL);
640 conn->ping[i].dest_id = id;
641 conn->ping[i].dest_name = name;
646 if (i >= conn->ping_count) {
647 i = conn->ping_count;
648 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
649 conn->ping[i].start_time = time(NULL);
650 conn->ping[i].dest_id = id;
651 conn->ping[i].dest_name = name;
655 /* Notify application */
659 silc_client_command_free(cmd);
662 SILC_CLIENT_CMD_FUNC(oper)
666 SILC_CLIENT_CMD_FUNC(trace)
670 SILC_CLIENT_CMD_FUNC(notice)
674 /* Command JOIN. Joins to a channel. */
676 SILC_CLIENT_CMD_FUNC(join)
678 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
679 SilcClientConnection conn = cmd->conn;
680 SilcIDCacheEntry id_cache = NULL;
681 SilcBuffer buffer, idp;
684 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
690 /* Show channels currently joined to */
695 /* See if we have joined to the requested channel already */
696 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
698 cmd->client->ops->say(cmd->client, conn,
699 "You are talking to channel %s", cmd->argv[1]);
700 conn->current_channel = (SilcChannelEntry)id_cache->context;
702 cmd->client->screen->bottom_line->channel = cmd->argv[1];
703 silc_screen_print_bottom_line(cmd->client->screen, 0);
708 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
710 /* Send JOIN command to the server */
713 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
714 1, cmd->argv[1], cmd->argv_lens[1],
715 2, idp->data, idp->len);
716 else if (cmd->argc == 3)
719 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
720 1, cmd->argv[1], cmd->argv_lens[1],
721 2, idp->data, idp->len,
722 3, cmd->argv[2], cmd->argv_lens[2]);
725 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
726 1, cmd->argv[1], cmd->argv_lens[1],
727 2, idp->data, idp->len,
728 3, cmd->argv[2], cmd->argv_lens[2],
729 4, cmd->argv[3], cmd->argv_lens[3]);
731 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
732 0, NULL, NULL, buffer->data, buffer->len, TRUE);
733 silc_buffer_free(buffer);
734 silc_buffer_free(idp);
736 /* Notify application */
740 silc_client_command_free(cmd);
743 /* MOTD command. Requests motd from server. */
745 SILC_CLIENT_CMD_FUNC(motd)
747 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
748 SilcClientConnection conn = cmd->conn;
752 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
757 if (cmd->argc < 1 || cmd->argc > 1) {
758 cmd->client->ops->say(cmd->client, conn,
764 /* Send TOPIC command to the server */
765 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
766 2, conn->remote_host,
767 strlen(conn->remote_host));
768 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
769 0, NULL, NULL, buffer->data, buffer->len, TRUE);
770 silc_buffer_free(buffer);
772 /* Notify application */
776 silc_client_command_free(cmd);
779 /* UMODE. Set user mode in SILC. */
781 SILC_CLIENT_CMD_FUNC(umode)
786 /* CMODE command. Sets channel mode. Modes that does not require any arguments
787 can be set several at once. Those modes that require argument must be set
788 separately (unless set with modes that does not require arguments). */
790 SILC_CLIENT_CMD_FUNC(cmode)
792 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
793 SilcClientConnection conn = cmd->conn;
794 SilcChannelEntry channel;
795 SilcBuffer buffer, chidp;
796 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
797 unsigned int mode, add, type, len, arg_len = 0;
801 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
807 cmd->client->ops->say(cmd->client, conn,
808 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
813 if (cmd->argv[1][0] == '*') {
814 if (!conn->current_channel) {
815 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
820 channel = conn->current_channel;
824 channel = silc_idlist_get_channel(cmd->client, conn, name);
826 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
832 mode = channel->mode;
834 /* Are we adding or removing mode */
835 if (cmd->argv[2][0] == '-')
840 /* Argument type to be sent to server */
844 cp = cmd->argv[2] + 1;
846 for (i = 0; i < len; i++) {
850 mode |= SILC_CHANNEL_MODE_PRIVATE;
852 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
856 mode |= SILC_CHANNEL_MODE_SECRET;
858 mode &= ~SILC_CHANNEL_MODE_SECRET;
862 mode |= SILC_CHANNEL_MODE_PRIVKEY;
864 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
868 mode |= SILC_CHANNEL_MODE_INVITE;
870 mode &= ~SILC_CHANNEL_MODE_INVITE;
874 mode |= SILC_CHANNEL_MODE_TOPIC;
876 mode &= ~SILC_CHANNEL_MODE_TOPIC;
881 mode |= SILC_CHANNEL_MODE_ULIMIT;
883 ll = atoi(cmd->argv[3]);
884 SILC_PUT32_MSB(ll, tmp);
888 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
893 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
896 arg_len = cmd->argv_lens[3];
898 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
903 mode |= SILC_CHANNEL_MODE_BAN;
906 arg_len = cmd->argv_lens[3];
908 mode &= ~SILC_CHANNEL_MODE_BAN;
913 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
916 arg_len = cmd->argv_lens[3];
918 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
923 mode |= SILC_CHANNEL_MODE_CIPHER;
926 arg_len = cmd->argv_lens[3];
928 mode &= ~SILC_CHANNEL_MODE_CIPHER;
938 if (type && cmd->argc < 3) {
943 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
944 SILC_PUT32_MSB(mode, modebuf);
946 /* Send the command packet. We support sending only one mode at once
947 that requires an argument. */
950 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
951 1, chidp->data, chidp->len,
952 2, modebuf, sizeof(modebuf),
956 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
957 1, chidp->data, chidp->len,
958 2, modebuf, sizeof(modebuf));
961 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
962 0, NULL, NULL, buffer->data, buffer->len, TRUE);
963 silc_buffer_free(buffer);
964 silc_buffer_free(chidp);
966 /* Notify application */
970 silc_client_command_free(cmd);
973 /* CUMODE command. Changes client's mode on a channel. */
975 SILC_CLIENT_CMD_FUNC(cumode)
977 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
978 SilcClientConnection conn = cmd->conn;
979 SilcChannelEntry channel;
981 SilcClientEntry client_entry;
982 SilcBuffer buffer, clidp, chidp;
983 unsigned char *name, *cp, modebuf[4];
984 unsigned int mode = 0, add, len;
985 char *nickname = NULL, *server = NULL;
986 unsigned int num = 0;
990 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
996 cmd->client->ops->say(cmd->client, conn,
997 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1002 if (cmd->argv[1][0] == '*') {
1003 if (!conn->current_channel) {
1004 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1009 channel = conn->current_channel;
1011 name = cmd->argv[1];
1013 channel = silc_idlist_get_channel(cmd->client, conn, name);
1015 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1021 /* Parse the typed nickname. */
1022 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1023 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1028 /* Find client entry */
1029 client_entry = silc_idlist_get_client(cmd->client, conn,
1030 nickname, server, num);
1031 if (!client_entry) {
1032 /* Client entry not found, it was requested thus mark this to be
1034 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1035 silc_client_command_destructor,
1036 silc_client_command_cumode,
1037 silc_client_command_dup(cmd));
1041 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1042 if (chu->client == client_entry) {
1048 /* Are we adding or removing mode */
1049 if (cmd->argv[2][0] == '-')
1055 cp = cmd->argv[2] + 1;
1057 for (i = 0; i < len; i++) {
1061 mode |= SILC_CHANNEL_UMODE_CHANFO;
1062 mode |= SILC_CHANNEL_UMODE_CHANOP;
1064 mode = SILC_CHANNEL_UMODE_NONE;
1069 mode |= SILC_CHANNEL_UMODE_CHANFO;
1071 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1075 mode |= SILC_CHANNEL_UMODE_CHANOP;
1077 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1086 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1087 SILC_PUT32_MSB(mode, modebuf);
1088 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1090 /* Send the command packet. We support sending only one mode at once
1091 that requires an argument. */
1092 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1093 1, chidp->data, chidp->len,
1095 3, clidp->data, clidp->len);
1097 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1098 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1099 silc_buffer_free(buffer);
1100 silc_buffer_free(chidp);
1101 silc_buffer_free(clidp);
1103 /* Notify application */
1107 silc_client_command_free(cmd);
1110 /* KICK command. Kicks a client out of channel. */
1112 SILC_CLIENT_CMD_FUNC(kick)
1114 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1115 SilcClientConnection conn = cmd->conn;
1119 SILC_CLIENT_CMD_FUNC(restart)
1123 SILC_CLIENT_CMD_FUNC(close)
1127 SILC_CLIENT_CMD_FUNC(die)
1131 SILC_CLIENT_CMD_FUNC(silcoper)
1135 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1137 SILC_CLIENT_CMD_FUNC(leave)
1139 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1140 SilcClientConnection conn = cmd->conn;
1141 SilcIDCacheEntry id_cache = NULL;
1142 SilcChannelEntry channel;
1143 SilcBuffer buffer, idp;
1147 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1152 if (cmd->argc != 2) {
1153 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1158 if (cmd->argv[1][0] == '*') {
1159 if (!conn->current_channel) {
1160 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1164 name = conn->current_channel->channel_name;
1166 name = cmd->argv[1];
1169 if (!conn->current_channel) {
1170 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1175 /* Get the Channel ID of the channel */
1176 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1177 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1182 channel = (SilcChannelEntry)id_cache->context;
1184 /* Send LEAVE command to the server */
1185 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1186 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1187 1, idp->data, idp->len);
1188 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1189 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1190 silc_buffer_free(buffer);
1191 silc_buffer_free(idp);
1193 /* We won't talk anymore on this channel */
1194 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1196 conn->current_channel = NULL;
1198 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1199 silc_free(channel->channel_name);
1200 silc_free(channel->id);
1201 silc_free(channel->key);
1202 silc_cipher_free(channel->channel_key);
1205 /* Notify application */
1209 silc_client_command_free(cmd);
1212 /* Command USERS. Requests the USERS of the clients joined on requested
1215 SILC_CLIENT_CMD_FUNC(users)
1217 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1218 SilcClientConnection conn = cmd->conn;
1219 SilcIDCacheEntry id_cache = NULL;
1220 SilcChannelEntry channel;
1221 SilcBuffer buffer, idp;
1225 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1230 if (cmd->argc != 2) {
1231 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1236 if (cmd->argv[1][0] == '*') {
1237 if (!conn->current_channel) {
1238 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1242 name = conn->current_channel->channel_name;
1244 name = cmd->argv[1];
1247 if (!conn->current_channel) {
1248 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1253 /* Get the Channel ID of the channel */
1254 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1255 /* XXX should resolve the channel ID; LIST command */
1256 cmd->client->ops->say(cmd->client, conn,
1257 "You are not on that channel", name);
1262 channel = (SilcChannelEntry)id_cache->context;
1264 if (!cmd->pending) {
1265 /* Send USERS command to the server */
1266 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1267 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1268 1, idp->data, idp->len);
1269 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1270 NULL, 0, NULL, NULL, buffer->data,
1272 silc_buffer_free(buffer);
1273 silc_buffer_free(idp);
1275 /* Register pending callback which will recall this command callback with
1276 same context and reprocesses the command. When reprocessing we actually
1277 display the information on the screen. */
1278 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1279 silc_client_command_destructor,
1280 silc_client_command_users,
1281 silc_client_command_dup(cmd));
1282 cmd->pending = TRUE;
1287 /* Pending command. Now we've resolved the information from server and
1288 we are ready to display the information on screen. */
1290 SilcChannelUser chu;
1292 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1293 channel->channel_name);
1295 silc_list_start(channel->clients);
1296 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1297 SilcClientEntry e = chu->client;
1298 char *m, tmp[80], line[80], len1;
1300 memset(line, 0, sizeof(line));
1301 memset(tmp, 0, sizeof(tmp));
1302 m = silc_client_chumode_char(chu->mode);
1305 strcat(line, e->nickname);
1306 strcat(line, e->server ? "@" : "");
1310 len1 = strlen(e->server);
1311 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1313 len1 = strlen(line);
1315 memset(&line[29], 0, len1 - 29);
1317 for (i = 0; i < 30 - len1 - 1; i++)
1322 strcat(tmp, m ? m : "");
1325 if (strlen(tmp) < 5)
1326 for (i = 0; i < 5 - strlen(tmp); i++)
1329 strcat(line, e->username ? e->username : "");
1331 cmd->client->ops->say(cmd->client, conn, "%s", line);
1338 /* Notify application */
1342 silc_client_command_free(cmd);