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 int argc, ...)
88 packet = silc_command_payload_encode_vap(command, 0, argc, ap);
89 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
90 NULL, 0, NULL, NULL, packet->data,
92 silc_buffer_free(packet);
95 /* Finds and returns a pointer to the command list. Return NULL if the
96 command is not found. */
98 SilcClientCommand *silc_client_command_find(const char *name)
100 SilcClientCommand *cmd;
102 for (cmd = silc_command_list; cmd->name; cmd++) {
103 if (!strcmp(cmd->name, name))
110 /* Add new pending command to be executed when reply to a command has been
111 received. The `reply_cmd' is the command that will call the `callback'
112 with `context' when reply has been received. If `ident is non-zero
113 the `callback' will be executed when received reply with command
114 identifier `ident'. */
116 void silc_client_command_pending(SilcClientConnection conn,
117 SilcCommand reply_cmd,
118 unsigned short ident,
119 SilcCommandCb callback,
122 SilcClientCommandPending *reply;
124 reply = silc_calloc(1, sizeof(*reply));
125 reply->reply_cmd = reply_cmd;
126 reply->ident = ident;
127 reply->context = context;
128 reply->callback = callback;
129 silc_dlist_add(conn->pending_commands, reply);
132 /* Deletes pending command by reply command type. */
134 void silc_client_command_pending_del(SilcClientConnection conn,
135 SilcCommand reply_cmd,
136 unsigned short ident)
138 SilcClientCommandPending *r;
140 silc_dlist_start(conn->pending_commands);
141 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
142 if (r->reply_cmd == reply_cmd && r->ident == ident) {
143 silc_dlist_del(conn->pending_commands, r);
149 /* Checks for pending commands and marks callbacks to be called from
150 the command reply function. Returns TRUE if there were pending command. */
152 int silc_client_command_pending_check(SilcClientConnection conn,
153 SilcClientCommandReplyContext ctx,
155 unsigned short ident)
157 SilcClientCommandPending *r;
159 silc_dlist_start(conn->pending_commands);
160 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
161 if (r->reply_cmd == command && r->ident == ident) {
162 ctx->context = r->context;
163 ctx->callback = r->callback;
172 /* Free command context and its internals */
174 void silc_client_command_free(SilcClientCommandContext cmd)
179 for (i = 0; i < cmd->argc; i++)
180 silc_free(cmd->argv[i]);
185 /* Command WHOIS. This command is used to query information about
188 SILC_CLIENT_CMD_FUNC(whois)
190 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
191 SilcClientConnection conn = cmd->conn;
195 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
200 if (cmd->argc < 2 || cmd->argc > 3) {
201 cmd->client->ops->say(cmd->client, conn,
202 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
207 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
208 cmd->argc - 1, ++cmd->argv,
209 ++cmd->argv_lens, ++cmd->argv_types,
211 silc_client_packet_send(cmd->client, cmd->conn->sock,
212 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
213 buffer->data, buffer->len, TRUE);
214 silc_buffer_free(buffer);
219 /* Notify application */
223 silc_client_command_free(cmd);
226 SILC_CLIENT_CMD_FUNC(whowas)
230 /* Command IDENTIFY. This command is used to query information about
231 specific user, especially ID's. */
233 SILC_CLIENT_CMD_FUNC(identify)
235 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
236 SilcClientConnection conn = cmd->conn;
240 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
245 if (cmd->argc < 2 || cmd->argc > 3) {
246 cmd->client->ops->say(cmd->client, conn,
247 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
252 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
253 cmd->argc - 1, ++cmd->argv,
254 ++cmd->argv_lens, ++cmd->argv_types,
256 silc_client_packet_send(cmd->client, cmd->conn->sock,
257 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
258 buffer->data, buffer->len, TRUE);
259 silc_buffer_free(buffer);
264 /* Notify application */
268 silc_client_command_free(cmd);
271 /* Command NICK. Shows current nickname/sets new nickname on current
274 SILC_CLIENT_CMD_FUNC(nick)
276 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
277 SilcClientConnection conn = cmd->conn;
281 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
286 if (!strcmp(conn->nickname, cmd->argv[1]))
289 /* Show current nickname */
292 cmd->client->ops->say(cmd->client, conn,
293 "Your nickname is %s on server %s",
294 conn->nickname, conn->remote_host);
296 cmd->client->ops->say(cmd->client, conn,
297 "Your nickname is %s", conn->nickname);
300 /* XXX Notify application */
305 /* Set new nickname */
306 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
307 cmd->argc - 1, ++cmd->argv,
308 ++cmd->argv_lens, ++cmd->argv_types,
310 silc_client_packet_send(cmd->client, cmd->conn->sock,
311 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
312 buffer->data, buffer->len, TRUE);
313 silc_buffer_free(buffer);
318 silc_free(conn->nickname);
319 conn->nickname = strdup(cmd->argv[1]);
321 /* Notify application */
325 silc_client_command_free(cmd);
328 SILC_CLIENT_CMD_FUNC(list)
332 /* Command TOPIC. Sets/shows topic on a channel. */
334 SILC_CLIENT_CMD_FUNC(topic)
336 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
337 SilcClientConnection conn = cmd->conn;
338 SilcIDCacheEntry id_cache = NULL;
339 SilcChannelEntry channel;
340 SilcBuffer buffer, idp;
344 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
349 if (cmd->argc < 2 || cmd->argc > 3) {
350 cmd->client->ops->say(cmd->client, conn,
351 "Usage: /TOPIC <channel> [<topic>]");
356 if (cmd->argv[1][0] == '*') {
357 if (!conn->current_channel) {
358 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
362 name = conn->current_channel->channel_name;
367 if (!conn->current_channel) {
368 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
373 /* Get the Channel ID of the channel */
374 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
375 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
380 channel = (SilcChannelEntry)id_cache->context;
382 /* Send TOPIC command to the server */
383 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
385 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
386 1, idp->data, idp->len,
388 strlen(cmd->argv[2]));
390 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
391 1, idp->data, idp->len,
393 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
394 0, NULL, NULL, buffer->data, buffer->len, TRUE);
395 silc_buffer_free(buffer);
396 silc_buffer_free(idp);
398 /* Notify application */
402 silc_client_command_free(cmd);
405 /* Command INVITE. Invites specific client to join a channel. */
407 SILC_CLIENT_CMD_FUNC(invite)
409 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
410 SilcClient client = cmd->client;
411 SilcClientConnection conn = cmd->conn;
412 SilcClientEntry client_entry;
413 SilcChannelEntry channel_entry;
414 SilcBuffer buffer, clidp, chidp;
415 unsigned int num = 0;
416 char *nickname = NULL, *server = NULL;
419 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
424 if (cmd->argc != 3) {
425 cmd->client->ops->say(cmd->client, conn,
426 "Usage: /INVITE <nickname>[@<server>] <channel>");
431 /* Parse the typed nickname. */
432 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
433 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
438 /* Find client entry */
439 client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
446 /* Client entry not found, it was requested thus mark this to be
448 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
449 silc_client_command_invite, context);
453 /* Find channel entry */
454 channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
455 if (!channel_entry) {
456 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
462 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
463 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
464 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
465 1, clidp->data, clidp->len,
466 2, chidp->data, chidp->len);
467 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
468 0, NULL, NULL, buffer->data, buffer->len, TRUE);
469 silc_buffer_free(buffer);
470 silc_buffer_free(clidp);
471 silc_buffer_free(chidp);
473 cmd->client->ops->say(cmd->client, conn,
474 "Inviting %s to channel %s", cmd->argv[1],
477 /* Notify application */
481 silc_client_command_free(cmd);
484 /* Command QUIT. Closes connection with current server. */
486 SILC_CLIENT_CMD_FUNC(quit)
488 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
492 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
497 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
498 ++cmd->argv, ++cmd->argv_lens,
499 ++cmd->argv_types, 0);
500 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
502 buffer->data, buffer->len, TRUE);
503 silc_buffer_free(buffer);
508 /* Close connection */
509 silc_client_close_connection(cmd->client, cmd->conn->sock);
510 cmd->client->ops->disconnect(cmd->client, cmd->conn);
512 /* Notify application */
516 silc_client_command_free(cmd);
519 SILC_CLIENT_CMD_FUNC(kill)
523 /* Command INFO. Request information about specific server. If specific
524 server is not provided the current server is used. */
526 SILC_CLIENT_CMD_FUNC(info)
528 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
529 SilcClientConnection conn = cmd->conn;
534 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
540 name = strdup(conn->remote_host);
542 name = strdup(cmd->argv[1]);
544 /* Send the command */
545 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
546 1, name, strlen(name));
547 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
548 0, NULL, NULL, buffer->data, buffer->len, TRUE);
549 silc_buffer_free(buffer);
551 /* Notify application */
555 silc_client_command_free(cmd);
558 SILC_CLIENT_CMD_FUNC(connect)
562 /* Command PING. Sends ping to server. This is used to test the
563 communication channel. */
565 SILC_CLIENT_CMD_FUNC(ping)
567 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
568 SilcClientConnection conn = cmd->conn;
575 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
580 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
581 name = strdup(conn->remote_host);
583 /* Send the command */
584 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
585 1, conn->remote_id_data,
587 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
588 0, NULL, NULL, buffer->data, buffer->len, TRUE);
589 silc_buffer_free(buffer);
591 id = silc_id_str2id(conn->remote_id_data, SILC_ID_SERVER);
593 /* Start counting time */
594 for (i = 0; i < conn->ping_count; i++) {
595 if (conn->ping[i].dest_id == NULL) {
596 conn->ping[i].start_time = time(NULL);
597 conn->ping[i].dest_id = id;
598 conn->ping[i].dest_name = name;
603 if (i >= conn->ping_count) {
604 i = conn->ping_count;
605 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
606 conn->ping[i].start_time = time(NULL);
607 conn->ping[i].dest_id = id;
608 conn->ping[i].dest_name = name;
612 /* Notify application */
616 silc_client_command_free(cmd);
619 SILC_CLIENT_CMD_FUNC(oper)
623 SILC_CLIENT_CMD_FUNC(trace)
627 SILC_CLIENT_CMD_FUNC(notice)
631 /* Command JOIN. Joins to a channel. */
633 SILC_CLIENT_CMD_FUNC(join)
635 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
636 SilcClientConnection conn = cmd->conn;
637 SilcIDCacheEntry id_cache = NULL;
638 SilcBuffer buffer, idp;
641 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
647 /* Show channels currently joined to */
652 /* See if we have joined to the requested channel already */
653 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
655 cmd->client->ops->say(cmd->client, conn,
656 "You are talking to channel %s", cmd->argv[1]);
657 conn->current_channel = (SilcChannelEntry)id_cache->context;
659 cmd->client->screen->bottom_line->channel = cmd->argv[1];
660 silc_screen_print_bottom_line(cmd->client->screen, 0);
665 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
667 /* Send JOIN command to the server */
670 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
671 1, cmd->argv[1], cmd->argv_lens[1],
672 2, idp->data, idp->len);
673 else if (cmd->argc == 3)
676 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
677 1, cmd->argv[1], cmd->argv_lens[1],
678 2, idp->data, idp->len,
679 3, cmd->argv[2], cmd->argv_lens[2]);
682 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
683 1, cmd->argv[1], cmd->argv_lens[1],
684 2, idp->data, idp->len,
685 3, cmd->argv[2], cmd->argv_lens[2],
686 4, cmd->argv[3], cmd->argv_lens[3]);
688 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
689 0, NULL, NULL, buffer->data, buffer->len, TRUE);
690 silc_buffer_free(buffer);
691 silc_buffer_free(idp);
693 /* Notify application */
697 silc_client_command_free(cmd);
700 /* MOTD command. Requests motd from server. */
702 SILC_CLIENT_CMD_FUNC(motd)
704 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
705 SilcClientConnection conn = cmd->conn;
709 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
714 if (cmd->argc < 1 || cmd->argc > 1) {
715 cmd->client->ops->say(cmd->client, conn,
721 /* Send TOPIC command to the server */
722 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
723 2, conn->remote_host,
724 strlen(conn->remote_host));
725 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
726 0, NULL, NULL, buffer->data, buffer->len, TRUE);
727 silc_buffer_free(buffer);
729 /* Notify application */
733 silc_client_command_free(cmd);
736 /* UMODE. Set user mode in SILC. */
738 SILC_CLIENT_CMD_FUNC(umode)
743 /* CMODE command. Sets channel mode. Modes that does not require any arguments
744 can be set several at once. Those modes that require argument must be set
745 separately (unless set with modes that does not require arguments). */
747 SILC_CLIENT_CMD_FUNC(cmode)
749 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
750 SilcClientConnection conn = cmd->conn;
751 SilcChannelEntry channel;
752 SilcBuffer buffer, chidp;
753 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
754 unsigned int mode, add, type, len, arg_len = 0;
758 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
764 cmd->client->ops->say(cmd->client, conn,
765 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
770 if (cmd->argv[1][0] == '*') {
771 if (!conn->current_channel) {
772 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
777 channel = conn->current_channel;
781 channel = silc_idlist_get_channel(cmd->client, conn, name);
783 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
789 mode = channel->mode;
791 /* Are we adding or removing mode */
792 if (cmd->argv[2][0] == '-')
797 /* Argument type to be sent to server */
801 cp = cmd->argv[2] + 1;
803 for (i = 0; i < len; i++) {
807 mode |= SILC_CHANNEL_MODE_PRIVATE;
809 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
813 mode |= SILC_CHANNEL_MODE_SECRET;
815 mode &= ~SILC_CHANNEL_MODE_SECRET;
819 mode |= SILC_CHANNEL_MODE_PRIVKEY;
821 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
825 mode |= SILC_CHANNEL_MODE_INVITE;
827 mode &= ~SILC_CHANNEL_MODE_INVITE;
831 mode |= SILC_CHANNEL_MODE_TOPIC;
833 mode &= ~SILC_CHANNEL_MODE_TOPIC;
838 mode |= SILC_CHANNEL_MODE_ULIMIT;
840 ll = atoi(cmd->argv[3]);
841 SILC_PUT32_MSB(ll, tmp);
845 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
850 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
853 arg_len = cmd->argv_lens[3];
855 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
860 mode |= SILC_CHANNEL_MODE_BAN;
863 arg_len = cmd->argv_lens[3];
865 mode &= ~SILC_CHANNEL_MODE_BAN;
870 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
873 arg_len = cmd->argv_lens[3];
875 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
880 mode |= SILC_CHANNEL_MODE_CIPHER;
883 arg_len = cmd->argv_lens[3];
885 mode &= ~SILC_CHANNEL_MODE_CIPHER;
895 if (type && cmd->argc < 3) {
900 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
901 SILC_PUT32_MSB(mode, modebuf);
903 /* Send the command packet. We support sending only one mode at once
904 that requires an argument. */
907 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
908 1, chidp->data, chidp->len,
909 2, modebuf, sizeof(modebuf),
913 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
914 1, chidp->data, chidp->len,
915 2, modebuf, sizeof(modebuf));
918 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
919 0, NULL, NULL, buffer->data, buffer->len, TRUE);
920 silc_buffer_free(buffer);
921 silc_buffer_free(chidp);
923 /* Notify application */
927 silc_client_command_free(cmd);
930 /* CUMODE command. Changes client's mode on a channel. */
932 SILC_CLIENT_CMD_FUNC(cumode)
934 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
935 SilcClientConnection conn = cmd->conn;
936 SilcChannelEntry channel;
938 SilcClientEntry client_entry;
939 SilcBuffer buffer, clidp, chidp;
940 unsigned char *name, *cp, modebuf[4];
941 unsigned int mode = 0, add, len;
942 char *nickname = NULL, *server = NULL;
943 unsigned int num = 0;
947 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
953 cmd->client->ops->say(cmd->client, conn,
954 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
959 if (cmd->argv[1][0] == '*') {
960 if (!conn->current_channel) {
961 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
966 channel = conn->current_channel;
970 channel = silc_idlist_get_channel(cmd->client, conn, name);
972 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
978 /* Parse the typed nickname. */
979 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
980 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
985 /* Find client entry */
986 client_entry = silc_idlist_get_client(cmd->client, conn,
987 nickname, server, num);
989 /* Client entry not found, it was requested thus mark this to be
991 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
992 silc_client_command_cumode, context);
996 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
997 if (chu->client == client_entry) {
1003 /* Are we adding or removing mode */
1004 if (cmd->argv[2][0] == '-')
1010 cp = cmd->argv[2] + 1;
1012 for (i = 0; i < len; i++) {
1016 mode |= SILC_CHANNEL_UMODE_CHANFO;
1017 mode |= SILC_CHANNEL_UMODE_CHANOP;
1019 mode = SILC_CHANNEL_UMODE_NONE;
1024 mode |= SILC_CHANNEL_UMODE_CHANFO;
1026 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1030 mode |= SILC_CHANNEL_UMODE_CHANOP;
1032 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1041 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1042 SILC_PUT32_MSB(mode, modebuf);
1043 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1045 /* Send the command packet. We support sending only one mode at once
1046 that requires an argument. */
1047 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1048 1, chidp->data, chidp->len,
1050 3, clidp->data, clidp->len);
1052 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1053 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1054 silc_buffer_free(buffer);
1055 silc_buffer_free(chidp);
1056 silc_buffer_free(clidp);
1058 /* Notify application */
1062 silc_client_command_free(cmd);
1065 /* KICK command. Kicks a client out of channel. */
1067 SILC_CLIENT_CMD_FUNC(kick)
1069 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1070 SilcClientConnection conn = cmd->conn;
1074 SILC_CLIENT_CMD_FUNC(restart)
1078 SILC_CLIENT_CMD_FUNC(close)
1082 SILC_CLIENT_CMD_FUNC(die)
1086 SILC_CLIENT_CMD_FUNC(silcoper)
1090 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1092 SILC_CLIENT_CMD_FUNC(leave)
1094 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1095 SilcClientConnection conn = cmd->conn;
1096 SilcIDCacheEntry id_cache = NULL;
1097 SilcChannelEntry channel;
1098 SilcBuffer buffer, idp;
1102 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1107 if (cmd->argc != 2) {
1108 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1113 if (cmd->argv[1][0] == '*') {
1114 if (!conn->current_channel) {
1115 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1119 name = conn->current_channel->channel_name;
1121 name = cmd->argv[1];
1124 if (!conn->current_channel) {
1125 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1130 /* Get the Channel ID of the channel */
1131 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1132 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1137 channel = (SilcChannelEntry)id_cache->context;
1139 /* Send LEAVE command to the server */
1140 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1141 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1142 1, idp->data, idp->len);
1143 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1144 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1145 silc_buffer_free(buffer);
1146 silc_buffer_free(idp);
1148 /* We won't talk anymore on this channel */
1149 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1151 conn->current_channel = NULL;
1153 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1154 silc_free(channel->channel_name);
1155 silc_free(channel->id);
1156 silc_free(channel->key);
1157 silc_cipher_free(channel->channel_key);
1160 /* Notify application */
1164 silc_client_command_free(cmd);
1167 /* Command USERS. Requests the USERS of the clients joined on requested
1170 SILC_CLIENT_CMD_FUNC(users)
1172 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1173 SilcClientConnection conn = cmd->conn;
1174 SilcIDCacheEntry id_cache = NULL;
1175 SilcChannelEntry channel;
1176 SilcBuffer buffer, idp;
1180 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1185 if (cmd->argc != 2) {
1186 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1191 if (cmd->argv[1][0] == '*') {
1192 if (!conn->current_channel) {
1193 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1197 name = conn->current_channel->channel_name;
1199 name = cmd->argv[1];
1202 if (!conn->current_channel) {
1203 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1208 /* Get the Channel ID of the channel */
1209 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1210 /* XXX should resolve the channel ID; LIST command */
1211 cmd->client->ops->say(cmd->client, conn,
1212 "You are not on that channel", name);
1217 channel = (SilcChannelEntry)id_cache->context;
1219 if (!cmd->pending) {
1220 /* Send USERS command to the server */
1221 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1222 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1223 1, idp->data, idp->len);
1224 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1225 NULL, 0, NULL, NULL, buffer->data,
1227 silc_buffer_free(buffer);
1228 silc_buffer_free(idp);
1230 /* Register pending callback which will recall this command callback with
1231 same context and reprocesses the command. When reprocessing we actually
1232 display the information on the screen. */
1233 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1234 silc_client_command_users, context);
1235 cmd->pending = TRUE;
1240 /* Pending command. Now we've resolved the information from server and
1241 we are ready to display the information on screen. */
1243 SilcChannelUser chu;
1245 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1246 channel->channel_name);
1248 silc_list_start(channel->clients);
1249 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1250 SilcClientEntry e = chu->client;
1251 char *m, tmp[80], line[80], len1;
1253 memset(line, 0, sizeof(line));
1254 memset(tmp, 0, sizeof(tmp));
1255 m = silc_client_chumode_char(chu->mode);
1258 strcat(line, e->nickname);
1259 strcat(line, e->server ? "@" : "");
1263 len1 = strlen(e->server);
1264 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1266 len1 = strlen(line);
1268 memset(&line[29], 0, len1 - 29);
1270 for (i = 0; i < 30 - len1 - 1; i++)
1275 strcat(tmp, m ? m : "");
1278 if (strlen(tmp) < 5)
1279 for (i = 0; i < 5 - strlen(tmp); i++)
1282 strcat(line, e->username ? e->username : "");
1284 cmd->client->ops->say(cmd->client, conn, "%s", line);
1291 /* Notify application */
1295 silc_client_command_free(cmd);