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 SilcCommandCb callback,
123 SilcClientCommandPending *reply;
125 reply = silc_calloc(1, sizeof(*reply));
126 reply->reply_cmd = reply_cmd;
127 reply->ident = ident;
128 reply->context = context;
129 reply->callback = callback;
130 silc_dlist_add(conn->pending_commands, reply);
133 /* Deletes pending command by reply command type. */
135 void silc_client_command_pending_del(SilcClientConnection conn,
136 SilcCommand reply_cmd,
137 unsigned short ident)
139 SilcClientCommandPending *r;
141 silc_dlist_start(conn->pending_commands);
142 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
143 if (r->reply_cmd == reply_cmd && r->ident == ident) {
144 silc_dlist_del(conn->pending_commands, r);
150 /* Checks for pending commands and marks callbacks to be called from
151 the command reply function. Returns TRUE if there were pending command. */
153 int silc_client_command_pending_check(SilcClientConnection conn,
154 SilcClientCommandReplyContext ctx,
156 unsigned short ident)
158 SilcClientCommandPending *r;
160 silc_dlist_start(conn->pending_commands);
161 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
162 if (r->reply_cmd == command && r->ident == ident) {
163 ctx->context = r->context;
164 ctx->callback = r->callback;
173 /* Free command context and its internals */
175 void silc_client_command_free(SilcClientCommandContext cmd)
180 for (i = 0; i < cmd->argc; i++)
181 silc_free(cmd->argv[i]);
186 /* Command WHOIS. This command is used to query information about
189 SILC_CLIENT_CMD_FUNC(whois)
191 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
192 SilcClientConnection conn = cmd->conn;
196 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
201 if (cmd->argc < 2 || cmd->argc > 3) {
202 cmd->client->ops->say(cmd->client, conn,
203 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
208 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
209 cmd->argc - 1, ++cmd->argv,
210 ++cmd->argv_lens, ++cmd->argv_types,
212 silc_client_packet_send(cmd->client, cmd->conn->sock,
213 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
214 buffer->data, buffer->len, TRUE);
215 silc_buffer_free(buffer);
220 /* Notify application */
224 silc_client_command_free(cmd);
227 SILC_CLIENT_CMD_FUNC(whowas)
231 /* Command IDENTIFY. This command is used to query information about
232 specific user, especially ID's. */
234 SILC_CLIENT_CMD_FUNC(identify)
236 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
237 SilcClientConnection conn = cmd->conn;
241 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
246 if (cmd->argc < 2 || cmd->argc > 3) {
247 cmd->client->ops->say(cmd->client, conn,
248 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
253 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
254 cmd->argc - 1, ++cmd->argv,
255 ++cmd->argv_lens, ++cmd->argv_types,
257 silc_client_packet_send(cmd->client, cmd->conn->sock,
258 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
259 buffer->data, buffer->len, TRUE);
260 silc_buffer_free(buffer);
265 /* Notify application */
269 silc_client_command_free(cmd);
272 /* Command NICK. Shows current nickname/sets new nickname on current
275 SILC_CLIENT_CMD_FUNC(nick)
277 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
278 SilcClientConnection conn = cmd->conn;
282 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
287 if (!strcmp(conn->nickname, cmd->argv[1]))
290 /* Show current nickname */
293 cmd->client->ops->say(cmd->client, conn,
294 "Your nickname is %s on server %s",
295 conn->nickname, conn->remote_host);
297 cmd->client->ops->say(cmd->client, conn,
298 "Your nickname is %s", conn->nickname);
301 /* XXX Notify application */
306 /* Set new nickname */
307 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
308 cmd->argc - 1, ++cmd->argv,
309 ++cmd->argv_lens, ++cmd->argv_types,
311 silc_client_packet_send(cmd->client, cmd->conn->sock,
312 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
313 buffer->data, buffer->len, TRUE);
314 silc_buffer_free(buffer);
319 silc_free(conn->nickname);
320 conn->nickname = strdup(cmd->argv[1]);
322 /* Notify application */
326 silc_client_command_free(cmd);
329 SILC_CLIENT_CMD_FUNC(list)
333 /* Command TOPIC. Sets/shows topic on a channel. */
335 SILC_CLIENT_CMD_FUNC(topic)
337 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
338 SilcClientConnection conn = cmd->conn;
339 SilcIDCacheEntry id_cache = NULL;
340 SilcChannelEntry channel;
341 SilcBuffer buffer, idp;
345 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
350 if (cmd->argc < 2 || cmd->argc > 3) {
351 cmd->client->ops->say(cmd->client, conn,
352 "Usage: /TOPIC <channel> [<topic>]");
357 if (cmd->argv[1][0] == '*') {
358 if (!conn->current_channel) {
359 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
363 name = conn->current_channel->channel_name;
368 if (!conn->current_channel) {
369 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
374 /* Get the Channel ID of the channel */
375 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
376 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
381 channel = (SilcChannelEntry)id_cache->context;
383 /* Send TOPIC command to the server */
384 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
386 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
387 1, idp->data, idp->len,
389 strlen(cmd->argv[2]));
391 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
392 1, idp->data, idp->len,
394 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
395 0, NULL, NULL, buffer->data, buffer->len, TRUE);
396 silc_buffer_free(buffer);
397 silc_buffer_free(idp);
399 /* Notify application */
403 silc_client_command_free(cmd);
406 /* Command INVITE. Invites specific client to join a channel. */
408 SILC_CLIENT_CMD_FUNC(invite)
410 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
411 SilcClient client = cmd->client;
412 SilcClientConnection conn = cmd->conn;
413 SilcClientEntry client_entry;
414 SilcChannelEntry channel_entry;
415 SilcBuffer buffer, clidp, chidp;
416 unsigned int num = 0;
417 char *nickname = NULL, *server = NULL;
420 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
425 if (cmd->argc != 3) {
426 cmd->client->ops->say(cmd->client, conn,
427 "Usage: /INVITE <nickname>[@<server>] <channel>");
432 /* Parse the typed nickname. */
433 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
434 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
439 /* Find client entry */
440 client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
447 /* Client entry not found, it was requested thus mark this to be
449 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
450 silc_client_command_invite, context);
454 /* Find channel entry */
455 channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
456 if (!channel_entry) {
457 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
463 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
464 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
465 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
466 1, clidp->data, clidp->len,
467 2, chidp->data, chidp->len);
468 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
469 0, NULL, NULL, buffer->data, buffer->len, TRUE);
470 silc_buffer_free(buffer);
471 silc_buffer_free(clidp);
472 silc_buffer_free(chidp);
474 cmd->client->ops->say(cmd->client, conn,
475 "Inviting %s to channel %s", cmd->argv[1],
478 /* Notify application */
482 silc_client_command_free(cmd);
485 /* Command QUIT. Closes connection with current server. */
487 SILC_CLIENT_CMD_FUNC(quit)
489 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
493 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
498 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
499 ++cmd->argv, ++cmd->argv_lens,
500 ++cmd->argv_types, 0);
501 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
503 buffer->data, buffer->len, TRUE);
504 silc_buffer_free(buffer);
509 /* Close connection */
510 silc_client_close_connection(cmd->client, cmd->conn->sock);
511 cmd->client->ops->disconnect(cmd->client, cmd->conn);
513 /* Notify application */
517 silc_client_command_free(cmd);
520 SILC_CLIENT_CMD_FUNC(kill)
524 /* Command INFO. Request information about specific server. If specific
525 server is not provided the current server is used. */
527 SILC_CLIENT_CMD_FUNC(info)
529 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
530 SilcClientConnection conn = cmd->conn;
535 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
541 name = strdup(conn->remote_host);
543 name = strdup(cmd->argv[1]);
545 /* Send the command */
546 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
547 1, name, strlen(name));
548 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
549 0, NULL, NULL, buffer->data, buffer->len, TRUE);
550 silc_buffer_free(buffer);
552 /* Notify application */
556 silc_client_command_free(cmd);
559 SILC_CLIENT_CMD_FUNC(connect)
563 /* Command PING. Sends ping to server. This is used to test the
564 communication channel. */
566 SILC_CLIENT_CMD_FUNC(ping)
568 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
569 SilcClientConnection conn = cmd->conn;
576 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
581 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
582 name = strdup(conn->remote_host);
584 /* Send the command */
585 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
586 1, conn->remote_id_data,
588 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
589 0, NULL, NULL, buffer->data, buffer->len, TRUE);
590 silc_buffer_free(buffer);
592 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
595 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
600 /* Start counting time */
601 for (i = 0; i < conn->ping_count; i++) {
602 if (conn->ping[i].dest_id == NULL) {
603 conn->ping[i].start_time = time(NULL);
604 conn->ping[i].dest_id = id;
605 conn->ping[i].dest_name = name;
610 if (i >= conn->ping_count) {
611 i = conn->ping_count;
612 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
613 conn->ping[i].start_time = time(NULL);
614 conn->ping[i].dest_id = id;
615 conn->ping[i].dest_name = name;
619 /* Notify application */
623 silc_client_command_free(cmd);
626 SILC_CLIENT_CMD_FUNC(oper)
630 SILC_CLIENT_CMD_FUNC(trace)
634 SILC_CLIENT_CMD_FUNC(notice)
638 /* Command JOIN. Joins to a channel. */
640 SILC_CLIENT_CMD_FUNC(join)
642 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
643 SilcClientConnection conn = cmd->conn;
644 SilcIDCacheEntry id_cache = NULL;
645 SilcBuffer buffer, idp;
648 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
654 /* Show channels currently joined to */
659 /* See if we have joined to the requested channel already */
660 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
662 cmd->client->ops->say(cmd->client, conn,
663 "You are talking to channel %s", cmd->argv[1]);
664 conn->current_channel = (SilcChannelEntry)id_cache->context;
666 cmd->client->screen->bottom_line->channel = cmd->argv[1];
667 silc_screen_print_bottom_line(cmd->client->screen, 0);
672 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
674 /* Send JOIN command to the server */
677 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
678 1, cmd->argv[1], cmd->argv_lens[1],
679 2, idp->data, idp->len);
680 else if (cmd->argc == 3)
683 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
684 1, cmd->argv[1], cmd->argv_lens[1],
685 2, idp->data, idp->len,
686 3, cmd->argv[2], cmd->argv_lens[2]);
689 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
690 1, cmd->argv[1], cmd->argv_lens[1],
691 2, idp->data, idp->len,
692 3, cmd->argv[2], cmd->argv_lens[2],
693 4, cmd->argv[3], cmd->argv_lens[3]);
695 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
696 0, NULL, NULL, buffer->data, buffer->len, TRUE);
697 silc_buffer_free(buffer);
698 silc_buffer_free(idp);
700 /* Notify application */
704 silc_client_command_free(cmd);
707 /* MOTD command. Requests motd from server. */
709 SILC_CLIENT_CMD_FUNC(motd)
711 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
712 SilcClientConnection conn = cmd->conn;
716 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
721 if (cmd->argc < 1 || cmd->argc > 1) {
722 cmd->client->ops->say(cmd->client, conn,
728 /* Send TOPIC command to the server */
729 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
730 2, conn->remote_host,
731 strlen(conn->remote_host));
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);
736 /* Notify application */
740 silc_client_command_free(cmd);
743 /* UMODE. Set user mode in SILC. */
745 SILC_CLIENT_CMD_FUNC(umode)
750 /* CMODE command. Sets channel mode. Modes that does not require any arguments
751 can be set several at once. Those modes that require argument must be set
752 separately (unless set with modes that does not require arguments). */
754 SILC_CLIENT_CMD_FUNC(cmode)
756 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
757 SilcClientConnection conn = cmd->conn;
758 SilcChannelEntry channel;
759 SilcBuffer buffer, chidp;
760 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
761 unsigned int mode, add, type, len, arg_len = 0;
765 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
771 cmd->client->ops->say(cmd->client, conn,
772 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
777 if (cmd->argv[1][0] == '*') {
778 if (!conn->current_channel) {
779 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
784 channel = conn->current_channel;
788 channel = silc_idlist_get_channel(cmd->client, conn, name);
790 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
796 mode = channel->mode;
798 /* Are we adding or removing mode */
799 if (cmd->argv[2][0] == '-')
804 /* Argument type to be sent to server */
808 cp = cmd->argv[2] + 1;
810 for (i = 0; i < len; i++) {
814 mode |= SILC_CHANNEL_MODE_PRIVATE;
816 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
820 mode |= SILC_CHANNEL_MODE_SECRET;
822 mode &= ~SILC_CHANNEL_MODE_SECRET;
826 mode |= SILC_CHANNEL_MODE_PRIVKEY;
828 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
832 mode |= SILC_CHANNEL_MODE_INVITE;
834 mode &= ~SILC_CHANNEL_MODE_INVITE;
838 mode |= SILC_CHANNEL_MODE_TOPIC;
840 mode &= ~SILC_CHANNEL_MODE_TOPIC;
845 mode |= SILC_CHANNEL_MODE_ULIMIT;
847 ll = atoi(cmd->argv[3]);
848 SILC_PUT32_MSB(ll, tmp);
852 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
857 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
860 arg_len = cmd->argv_lens[3];
862 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
867 mode |= SILC_CHANNEL_MODE_BAN;
870 arg_len = cmd->argv_lens[3];
872 mode &= ~SILC_CHANNEL_MODE_BAN;
877 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
880 arg_len = cmd->argv_lens[3];
882 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
887 mode |= SILC_CHANNEL_MODE_CIPHER;
890 arg_len = cmd->argv_lens[3];
892 mode &= ~SILC_CHANNEL_MODE_CIPHER;
902 if (type && cmd->argc < 3) {
907 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
908 SILC_PUT32_MSB(mode, modebuf);
910 /* Send the command packet. We support sending only one mode at once
911 that requires an argument. */
914 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
915 1, chidp->data, chidp->len,
916 2, modebuf, sizeof(modebuf),
920 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
921 1, chidp->data, chidp->len,
922 2, modebuf, sizeof(modebuf));
925 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
926 0, NULL, NULL, buffer->data, buffer->len, TRUE);
927 silc_buffer_free(buffer);
928 silc_buffer_free(chidp);
930 /* Notify application */
934 silc_client_command_free(cmd);
937 /* CUMODE command. Changes client's mode on a channel. */
939 SILC_CLIENT_CMD_FUNC(cumode)
941 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
942 SilcClientConnection conn = cmd->conn;
943 SilcChannelEntry channel;
945 SilcClientEntry client_entry;
946 SilcBuffer buffer, clidp, chidp;
947 unsigned char *name, *cp, modebuf[4];
948 unsigned int mode = 0, add, len;
949 char *nickname = NULL, *server = NULL;
950 unsigned int num = 0;
954 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
960 cmd->client->ops->say(cmd->client, conn,
961 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
966 if (cmd->argv[1][0] == '*') {
967 if (!conn->current_channel) {
968 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
973 channel = conn->current_channel;
977 channel = silc_idlist_get_channel(cmd->client, conn, name);
979 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
985 /* Parse the typed nickname. */
986 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
987 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
992 /* Find client entry */
993 client_entry = silc_idlist_get_client(cmd->client, conn,
994 nickname, server, num);
996 /* Client entry not found, it was requested thus mark this to be
998 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
999 silc_client_command_cumode, context);
1003 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1004 if (chu->client == client_entry) {
1010 /* Are we adding or removing mode */
1011 if (cmd->argv[2][0] == '-')
1017 cp = cmd->argv[2] + 1;
1019 for (i = 0; i < len; i++) {
1023 mode |= SILC_CHANNEL_UMODE_CHANFO;
1024 mode |= SILC_CHANNEL_UMODE_CHANOP;
1026 mode = SILC_CHANNEL_UMODE_NONE;
1031 mode |= SILC_CHANNEL_UMODE_CHANFO;
1033 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1037 mode |= SILC_CHANNEL_UMODE_CHANOP;
1039 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1048 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1049 SILC_PUT32_MSB(mode, modebuf);
1050 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1052 /* Send the command packet. We support sending only one mode at once
1053 that requires an argument. */
1054 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1055 1, chidp->data, chidp->len,
1057 3, clidp->data, clidp->len);
1059 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1060 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1061 silc_buffer_free(buffer);
1062 silc_buffer_free(chidp);
1063 silc_buffer_free(clidp);
1065 /* Notify application */
1069 silc_client_command_free(cmd);
1072 /* KICK command. Kicks a client out of channel. */
1074 SILC_CLIENT_CMD_FUNC(kick)
1076 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1077 SilcClientConnection conn = cmd->conn;
1081 SILC_CLIENT_CMD_FUNC(restart)
1085 SILC_CLIENT_CMD_FUNC(close)
1089 SILC_CLIENT_CMD_FUNC(die)
1093 SILC_CLIENT_CMD_FUNC(silcoper)
1097 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1099 SILC_CLIENT_CMD_FUNC(leave)
1101 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1102 SilcClientConnection conn = cmd->conn;
1103 SilcIDCacheEntry id_cache = NULL;
1104 SilcChannelEntry channel;
1105 SilcBuffer buffer, idp;
1109 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1114 if (cmd->argc != 2) {
1115 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1120 if (cmd->argv[1][0] == '*') {
1121 if (!conn->current_channel) {
1122 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1126 name = conn->current_channel->channel_name;
1128 name = cmd->argv[1];
1131 if (!conn->current_channel) {
1132 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1137 /* Get the Channel ID of the channel */
1138 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1139 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1144 channel = (SilcChannelEntry)id_cache->context;
1146 /* Send LEAVE command to the server */
1147 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1148 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1149 1, idp->data, idp->len);
1150 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1151 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1152 silc_buffer_free(buffer);
1153 silc_buffer_free(idp);
1155 /* We won't talk anymore on this channel */
1156 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1158 conn->current_channel = NULL;
1160 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1161 silc_free(channel->channel_name);
1162 silc_free(channel->id);
1163 silc_free(channel->key);
1164 silc_cipher_free(channel->channel_key);
1167 /* Notify application */
1171 silc_client_command_free(cmd);
1174 /* Command USERS. Requests the USERS of the clients joined on requested
1177 SILC_CLIENT_CMD_FUNC(users)
1179 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1180 SilcClientConnection conn = cmd->conn;
1181 SilcIDCacheEntry id_cache = NULL;
1182 SilcChannelEntry channel;
1183 SilcBuffer buffer, idp;
1187 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1192 if (cmd->argc != 2) {
1193 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1198 if (cmd->argv[1][0] == '*') {
1199 if (!conn->current_channel) {
1200 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1204 name = conn->current_channel->channel_name;
1206 name = cmd->argv[1];
1209 if (!conn->current_channel) {
1210 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1215 /* Get the Channel ID of the channel */
1216 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1217 /* XXX should resolve the channel ID; LIST command */
1218 cmd->client->ops->say(cmd->client, conn,
1219 "You are not on that channel", name);
1224 channel = (SilcChannelEntry)id_cache->context;
1226 if (!cmd->pending) {
1227 /* Send USERS command to the server */
1228 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1229 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1230 1, idp->data, idp->len);
1231 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1232 NULL, 0, NULL, NULL, buffer->data,
1234 silc_buffer_free(buffer);
1235 silc_buffer_free(idp);
1237 /* Register pending callback which will recall this command callback with
1238 same context and reprocesses the command. When reprocessing we actually
1239 display the information on the screen. */
1240 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1241 silc_client_command_users, context);
1242 cmd->pending = TRUE;
1247 /* Pending command. Now we've resolved the information from server and
1248 we are ready to display the information on screen. */
1250 SilcChannelUser chu;
1252 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1253 channel->channel_name);
1255 silc_list_start(channel->clients);
1256 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1257 SilcClientEntry e = chu->client;
1258 char *m, tmp[80], line[80], len1;
1260 memset(line, 0, sizeof(line));
1261 memset(tmp, 0, sizeof(tmp));
1262 m = silc_client_chumode_char(chu->mode);
1265 strcat(line, e->nickname);
1266 strcat(line, e->server ? "@" : "");
1270 len1 = strlen(e->server);
1271 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1273 len1 = strlen(line);
1275 memset(&line[29], 0, len1 - 29);
1277 for (i = 0; i < 30 - len1 - 1; i++)
1282 strcat(tmp, m ? m : "");
1285 if (strlen(tmp) < 5)
1286 for (i = 0; i < 5 - strlen(tmp); i++)
1289 strcat(line, e->username ? e->username : "");
1291 cmd->client->ops->say(cmd->client, conn, "%s", line);
1298 /* Notify application */
1302 silc_client_command_free(cmd);