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, 2),
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);
524 SilcClientConnection conn;
527 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
529 QuitInternal q = (QuitInternal)context;
531 /* Close connection */
532 q->client->ops->disconnect(q->client, q->conn);
533 silc_client_close_connection(q->client, q->conn->sock);
538 /* Command QUIT. Closes connection with current server. */
540 SILC_CLIENT_CMD_FUNC(quit)
542 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
547 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
553 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
554 &cmd->argv[1], &cmd->argv_lens[1],
555 &cmd->argv_types[1], 0);
557 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
558 NULL, NULL, NULL, 0);
559 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
561 buffer->data, buffer->len, TRUE);
562 silc_buffer_free(buffer);
564 q = silc_calloc(1, sizeof(*q));
565 q->client = cmd->client;
568 /* We quit the connection with little timeout */
569 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
570 silc_client_command_quit_cb, (void *)q,
571 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
573 /* Notify application */
577 silc_client_command_free(cmd);
580 SILC_CLIENT_CMD_FUNC(kill)
584 /* Command INFO. Request information about specific server. If specific
585 server is not provided the current server is used. */
587 SILC_CLIENT_CMD_FUNC(info)
589 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
590 SilcClientConnection conn = cmd->conn;
595 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
601 name = strdup(conn->remote_host);
603 name = strdup(cmd->argv[1]);
605 /* Send the command */
606 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
607 1, name, strlen(name));
608 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
609 0, NULL, NULL, buffer->data, buffer->len, TRUE);
610 silc_buffer_free(buffer);
612 /* Notify application */
616 silc_client_command_free(cmd);
619 SILC_CLIENT_CMD_FUNC(connect)
623 /* Command PING. Sends ping to server. This is used to test the
624 communication channel. */
626 SILC_CLIENT_CMD_FUNC(ping)
628 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
629 SilcClientConnection conn = cmd->conn;
636 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
641 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
642 name = strdup(conn->remote_host);
644 /* Send the command */
645 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
646 1, conn->remote_id_data,
648 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
649 0, NULL, NULL, buffer->data, buffer->len, TRUE);
650 silc_buffer_free(buffer);
652 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
655 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
660 /* Start counting time */
661 for (i = 0; i < conn->ping_count; i++) {
662 if (conn->ping[i].dest_id == NULL) {
663 conn->ping[i].start_time = time(NULL);
664 conn->ping[i].dest_id = id;
665 conn->ping[i].dest_name = name;
670 if (i >= conn->ping_count) {
671 i = conn->ping_count;
672 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
673 conn->ping[i].start_time = time(NULL);
674 conn->ping[i].dest_id = id;
675 conn->ping[i].dest_name = name;
679 /* Notify application */
683 silc_client_command_free(cmd);
686 SILC_CLIENT_CMD_FUNC(oper)
690 SILC_CLIENT_CMD_FUNC(trace)
694 SILC_CLIENT_CMD_FUNC(notice)
698 /* Command JOIN. Joins to a channel. */
700 SILC_CLIENT_CMD_FUNC(join)
702 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
703 SilcClientConnection conn = cmd->conn;
704 SilcIDCacheEntry id_cache = NULL;
705 SilcBuffer buffer, idp;
708 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
714 /* Show channels currently joined to */
719 /* See if we have joined to the requested channel already */
720 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
722 cmd->client->ops->say(cmd->client, conn,
723 "You are talking to channel %s", cmd->argv[1]);
724 conn->current_channel = (SilcChannelEntry)id_cache->context;
726 cmd->client->screen->bottom_line->channel = cmd->argv[1];
727 silc_screen_print_bottom_line(cmd->client->screen, 0);
732 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
734 /* Send JOIN command to the server */
737 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
738 1, cmd->argv[1], cmd->argv_lens[1],
739 2, idp->data, idp->len);
740 else if (cmd->argc == 3)
743 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
744 1, cmd->argv[1], cmd->argv_lens[1],
745 2, idp->data, idp->len,
746 3, cmd->argv[2], cmd->argv_lens[2]);
749 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
750 1, cmd->argv[1], cmd->argv_lens[1],
751 2, idp->data, idp->len,
752 3, cmd->argv[2], cmd->argv_lens[2],
753 4, cmd->argv[3], cmd->argv_lens[3]);
755 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
756 0, NULL, NULL, buffer->data, buffer->len, TRUE);
757 silc_buffer_free(buffer);
758 silc_buffer_free(idp);
760 /* Notify application */
764 silc_client_command_free(cmd);
767 /* MOTD command. Requests motd from server. */
769 SILC_CLIENT_CMD_FUNC(motd)
771 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
772 SilcClientConnection conn = cmd->conn;
776 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
781 if (cmd->argc < 1 || cmd->argc > 1) {
782 cmd->client->ops->say(cmd->client, conn,
788 /* Send TOPIC command to the server */
789 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
790 2, conn->remote_host,
791 strlen(conn->remote_host));
792 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
793 0, NULL, NULL, buffer->data, buffer->len, TRUE);
794 silc_buffer_free(buffer);
796 /* Notify application */
800 silc_client_command_free(cmd);
803 /* UMODE. Set user mode in SILC. */
805 SILC_CLIENT_CMD_FUNC(umode)
810 /* CMODE command. Sets channel mode. Modes that does not require any arguments
811 can be set several at once. Those modes that require argument must be set
812 separately (unless set with modes that does not require arguments). */
814 SILC_CLIENT_CMD_FUNC(cmode)
816 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
817 SilcClientConnection conn = cmd->conn;
818 SilcChannelEntry channel;
819 SilcBuffer buffer, chidp;
820 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
821 unsigned int mode, add, type, len, arg_len = 0;
825 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
831 cmd->client->ops->say(cmd->client, conn,
832 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
837 if (cmd->argv[1][0] == '*') {
838 if (!conn->current_channel) {
839 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
844 channel = conn->current_channel;
848 channel = silc_idlist_get_channel(cmd->client, conn, name);
850 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
856 mode = channel->mode;
858 /* Are we adding or removing mode */
859 if (cmd->argv[2][0] == '-')
864 /* Argument type to be sent to server */
868 cp = cmd->argv[2] + 1;
870 for (i = 0; i < len; i++) {
874 mode |= SILC_CHANNEL_MODE_PRIVATE;
876 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
880 mode |= SILC_CHANNEL_MODE_SECRET;
882 mode &= ~SILC_CHANNEL_MODE_SECRET;
886 mode |= SILC_CHANNEL_MODE_PRIVKEY;
888 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
892 mode |= SILC_CHANNEL_MODE_INVITE;
894 mode &= ~SILC_CHANNEL_MODE_INVITE;
898 mode |= SILC_CHANNEL_MODE_TOPIC;
900 mode &= ~SILC_CHANNEL_MODE_TOPIC;
905 mode |= SILC_CHANNEL_MODE_ULIMIT;
907 ll = atoi(cmd->argv[3]);
908 SILC_PUT32_MSB(ll, tmp);
912 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
917 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
920 arg_len = cmd->argv_lens[3];
922 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
927 mode |= SILC_CHANNEL_MODE_BAN;
930 arg_len = cmd->argv_lens[3];
932 mode &= ~SILC_CHANNEL_MODE_BAN;
937 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
940 arg_len = cmd->argv_lens[3];
942 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
947 mode |= SILC_CHANNEL_MODE_CIPHER;
950 arg_len = cmd->argv_lens[3];
952 mode &= ~SILC_CHANNEL_MODE_CIPHER;
962 if (type && cmd->argc < 3) {
967 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
968 SILC_PUT32_MSB(mode, modebuf);
970 /* Send the command packet. We support sending only one mode at once
971 that requires an argument. */
974 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
975 1, chidp->data, chidp->len,
976 2, modebuf, sizeof(modebuf),
980 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
981 1, chidp->data, chidp->len,
982 2, modebuf, sizeof(modebuf));
985 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
986 0, NULL, NULL, buffer->data, buffer->len, TRUE);
987 silc_buffer_free(buffer);
988 silc_buffer_free(chidp);
990 /* Notify application */
994 silc_client_command_free(cmd);
997 /* CUMODE command. Changes client's mode on a channel. */
999 SILC_CLIENT_CMD_FUNC(cumode)
1001 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1002 SilcClientConnection conn = cmd->conn;
1003 SilcChannelEntry channel;
1004 SilcChannelUser chu;
1005 SilcClientEntry client_entry;
1006 SilcBuffer buffer, clidp, chidp;
1007 unsigned char *name, *cp, modebuf[4];
1008 unsigned int mode = 0, add, len;
1009 char *nickname = NULL, *server = NULL;
1010 unsigned int num = 0;
1014 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1019 if (cmd->argc < 4) {
1020 cmd->client->ops->say(cmd->client, conn,
1021 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1026 if (cmd->argv[1][0] == '*') {
1027 if (!conn->current_channel) {
1028 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1033 channel = conn->current_channel;
1035 name = cmd->argv[1];
1037 channel = silc_idlist_get_channel(cmd->client, conn, name);
1039 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1045 /* Parse the typed nickname. */
1046 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1047 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1052 /* Find client entry */
1053 client_entry = silc_idlist_get_client(cmd->client, conn,
1054 nickname, server, num);
1055 if (!client_entry) {
1056 /* Client entry not found, it was requested thus mark this to be
1058 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1059 silc_client_command_destructor,
1060 silc_client_command_cumode,
1061 silc_client_command_dup(cmd));
1066 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1067 if (chu->client == client_entry) {
1073 /* Are we adding or removing mode */
1074 if (cmd->argv[2][0] == '-')
1080 cp = cmd->argv[2] + 1;
1082 for (i = 0; i < len; i++) {
1086 mode |= SILC_CHANNEL_UMODE_CHANFO;
1087 mode |= SILC_CHANNEL_UMODE_CHANOP;
1089 mode = SILC_CHANNEL_UMODE_NONE;
1094 mode |= SILC_CHANNEL_UMODE_CHANFO;
1096 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1100 mode |= SILC_CHANNEL_UMODE_CHANOP;
1102 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1111 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1112 SILC_PUT32_MSB(mode, modebuf);
1113 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1115 /* Send the command packet. We support sending only one mode at once
1116 that requires an argument. */
1117 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1118 1, chidp->data, chidp->len,
1120 3, clidp->data, clidp->len);
1122 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1123 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1124 silc_buffer_free(buffer);
1125 silc_buffer_free(chidp);
1126 silc_buffer_free(clidp);
1128 /* Notify application */
1132 silc_client_command_free(cmd);
1135 /* KICK command. Kicks a client out of channel. */
1137 SILC_CLIENT_CMD_FUNC(kick)
1139 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1140 SilcClientConnection conn = cmd->conn;
1144 SILC_CLIENT_CMD_FUNC(restart)
1148 SILC_CLIENT_CMD_FUNC(close)
1152 SILC_CLIENT_CMD_FUNC(die)
1156 SILC_CLIENT_CMD_FUNC(silcoper)
1160 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1162 SILC_CLIENT_CMD_FUNC(leave)
1164 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1165 SilcClientConnection conn = cmd->conn;
1166 SilcIDCacheEntry id_cache = NULL;
1167 SilcChannelEntry channel;
1168 SilcBuffer buffer, idp;
1172 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1177 if (cmd->argc != 2) {
1178 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1183 if (cmd->argv[1][0] == '*') {
1184 if (!conn->current_channel) {
1185 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1189 name = conn->current_channel->channel_name;
1191 name = cmd->argv[1];
1194 if (!conn->current_channel) {
1195 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1200 /* Get the Channel ID of the channel */
1201 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1202 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1207 channel = (SilcChannelEntry)id_cache->context;
1209 /* Send LEAVE command to the server */
1210 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1211 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1212 1, idp->data, idp->len);
1213 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1214 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1215 silc_buffer_free(buffer);
1216 silc_buffer_free(idp);
1218 /* We won't talk anymore on this channel */
1219 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1221 conn->current_channel = NULL;
1223 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1224 silc_free(channel->channel_name);
1225 silc_free(channel->id);
1226 silc_free(channel->key);
1227 silc_cipher_free(channel->channel_key);
1230 /* Notify application */
1234 silc_client_command_free(cmd);
1237 /* Command USERS. Requests the USERS of the clients joined on requested
1240 SILC_CLIENT_CMD_FUNC(users)
1242 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1243 SilcClientConnection conn = cmd->conn;
1244 SilcIDCacheEntry id_cache = NULL;
1245 SilcChannelEntry channel;
1246 SilcBuffer buffer, idp;
1247 char *name, *line = NULL;
1248 unsigned int line_len = 0;
1251 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1256 if (cmd->argc != 2) {
1257 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1262 if (cmd->argv[1][0] == '*') {
1263 if (!conn->current_channel) {
1264 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1268 name = conn->current_channel->channel_name;
1270 name = cmd->argv[1];
1273 if (!conn->current_channel) {
1274 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1279 /* Get the Channel ID of the channel */
1280 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1281 /* XXX should resolve the channel ID; LIST command */
1282 cmd->client->ops->say(cmd->client, conn,
1283 "You are not on that channel", name);
1288 channel = (SilcChannelEntry)id_cache->context;
1290 if (!cmd->pending) {
1291 /* Send USERS command to the server */
1292 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1293 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1294 1, idp->data, idp->len);
1295 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1296 NULL, 0, NULL, NULL, buffer->data,
1298 silc_buffer_free(buffer);
1299 silc_buffer_free(idp);
1301 /* Register pending callback which will recall this command callback with
1302 same context and reprocesses the command. When reprocessing we actually
1303 display the information on the screen. */
1304 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1305 silc_client_command_destructor,
1306 silc_client_command_users,
1307 silc_client_command_dup(cmd));
1308 cmd->pending = TRUE;
1313 /* Pending command. Now we've resolved the information from server and
1314 we are ready to display the information on screen. */
1316 SilcChannelUser chu;
1318 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1319 channel->channel_name);
1321 line = silc_calloc(4096, sizeof(*line));
1323 silc_list_start(channel->clients);
1324 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1325 SilcClientEntry e = chu->client;
1326 char *m, tmp[80], len1;
1328 memset(line, 0, sizeof(line_len));
1330 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1332 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1333 line = silc_calloc(line_len, sizeof(*line));
1336 memset(tmp, 0, sizeof(tmp));
1337 m = silc_client_chumode_char(chu->mode);
1339 strncat(line, " ", 1);
1340 strncat(line, e->nickname, strlen(e->nickname));
1341 strncat(line, e->server ? "@" : "", 1);
1345 len1 = strlen(e->server);
1346 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1348 len1 = strlen(line);
1350 memset(&line[29], 0, len1 - 29);
1352 for (i = 0; i < 30 - len1 - 1; i++)
1356 strncat(line, " H", 3);
1357 strcat(tmp, m ? m : "");
1358 strncat(line, tmp, strlen(tmp));
1360 if (strlen(tmp) < 5)
1361 for (i = 0; i < 5 - strlen(tmp); i++)
1364 strcat(line, e->username ? e->username : "");
1366 cmd->client->ops->say(cmd->client, conn, "%s", line);
1376 /* Notify application */
1380 silc_client_command_free(cmd);