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, 4),
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,
482 /* Client entry not found, it was requested thus mark this to be
484 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
485 silc_client_command_destructor,
486 silc_client_command_invite,
487 silc_client_command_dup(cmd));
492 /* Find channel entry */
493 channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
494 if (!channel_entry) {
495 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
501 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
502 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
503 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
504 1, clidp->data, clidp->len,
505 2, chidp->data, chidp->len);
506 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
507 0, NULL, NULL, buffer->data, buffer->len, TRUE);
508 silc_buffer_free(buffer);
509 silc_buffer_free(clidp);
510 silc_buffer_free(chidp);
512 cmd->client->ops->say(cmd->client, conn,
513 "Inviting %s to channel %s", cmd->argv[1],
516 /* Notify application */
520 silc_client_command_free(cmd);
525 SilcClientConnection conn;
528 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
530 QuitInternal q = (QuitInternal)context;
532 /* Close connection */
533 q->client->ops->disconnect(q->client, q->conn);
534 silc_client_close_connection(q->client, q->conn->sock);
539 /* Command QUIT. Closes connection with current server. */
541 SILC_CLIENT_CMD_FUNC(quit)
543 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
548 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
554 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
555 &cmd->argv[1], &cmd->argv_lens[1],
556 &cmd->argv_types[1], 0);
558 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
559 NULL, NULL, NULL, 0);
560 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
562 buffer->data, buffer->len, TRUE);
563 silc_buffer_free(buffer);
565 q = silc_calloc(1, sizeof(*q));
566 q->client = cmd->client;
569 /* We quit the connection with little timeout */
570 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
571 silc_client_command_quit_cb, (void *)q,
572 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
574 /* Notify application */
578 silc_client_command_free(cmd);
581 SILC_CLIENT_CMD_FUNC(kill)
585 /* Command INFO. Request information about specific server. If specific
586 server is not provided the current server is used. */
588 SILC_CLIENT_CMD_FUNC(info)
590 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
591 SilcClientConnection conn = cmd->conn;
596 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
602 name = strdup(conn->remote_host);
604 name = strdup(cmd->argv[1]);
606 /* Send the command */
607 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
608 1, name, strlen(name));
609 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
610 0, NULL, NULL, buffer->data, buffer->len, TRUE);
611 silc_buffer_free(buffer);
613 /* Notify application */
617 silc_client_command_free(cmd);
620 SILC_CLIENT_CMD_FUNC(connect)
624 /* Command PING. Sends ping to server. This is used to test the
625 communication channel. */
627 SILC_CLIENT_CMD_FUNC(ping)
629 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
630 SilcClientConnection conn = cmd->conn;
637 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
642 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
643 name = strdup(conn->remote_host);
645 /* Send the command */
646 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
647 1, conn->remote_id_data,
649 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
650 0, NULL, NULL, buffer->data, buffer->len, TRUE);
651 silc_buffer_free(buffer);
653 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
656 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
661 /* Start counting time */
662 for (i = 0; i < conn->ping_count; i++) {
663 if (conn->ping[i].dest_id == NULL) {
664 conn->ping[i].start_time = time(NULL);
665 conn->ping[i].dest_id = id;
666 conn->ping[i].dest_name = name;
671 if (i >= conn->ping_count) {
672 i = conn->ping_count;
673 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
674 conn->ping[i].start_time = time(NULL);
675 conn->ping[i].dest_id = id;
676 conn->ping[i].dest_name = name;
680 /* Notify application */
684 silc_client_command_free(cmd);
687 SILC_CLIENT_CMD_FUNC(oper)
691 SILC_CLIENT_CMD_FUNC(trace)
695 SILC_CLIENT_CMD_FUNC(notice)
699 /* Command JOIN. Joins to a channel. */
701 SILC_CLIENT_CMD_FUNC(join)
703 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
704 SilcClientConnection conn = cmd->conn;
705 SilcIDCacheEntry id_cache = NULL;
706 SilcBuffer buffer, idp;
709 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
715 /* Show channels currently joined to */
720 /* See if we have joined to the requested channel already */
721 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
723 cmd->client->ops->say(cmd->client, conn,
724 "You are talking to channel %s", cmd->argv[1]);
725 conn->current_channel = (SilcChannelEntry)id_cache->context;
727 cmd->client->screen->bottom_line->channel = cmd->argv[1];
728 silc_screen_print_bottom_line(cmd->client->screen, 0);
733 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
735 /* Send JOIN command to the server */
738 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
739 1, cmd->argv[1], cmd->argv_lens[1],
740 2, idp->data, idp->len);
741 else if (cmd->argc == 3)
744 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
745 1, cmd->argv[1], cmd->argv_lens[1],
746 2, idp->data, idp->len,
747 3, cmd->argv[2], cmd->argv_lens[2]);
750 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
751 1, cmd->argv[1], cmd->argv_lens[1],
752 2, idp->data, idp->len,
753 3, cmd->argv[2], cmd->argv_lens[2],
754 4, cmd->argv[3], cmd->argv_lens[3]);
756 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
757 0, NULL, NULL, buffer->data, buffer->len, TRUE);
758 silc_buffer_free(buffer);
759 silc_buffer_free(idp);
761 /* Notify application */
765 silc_client_command_free(cmd);
768 /* MOTD command. Requests motd from server. */
770 SILC_CLIENT_CMD_FUNC(motd)
772 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
773 SilcClientConnection conn = cmd->conn;
777 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
782 if (cmd->argc < 1 || cmd->argc > 1) {
783 cmd->client->ops->say(cmd->client, conn,
789 /* Send TOPIC command to the server */
790 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
791 2, conn->remote_host,
792 strlen(conn->remote_host));
793 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
794 0, NULL, NULL, buffer->data, buffer->len, TRUE);
795 silc_buffer_free(buffer);
797 /* Notify application */
801 silc_client_command_free(cmd);
804 /* UMODE. Set user mode in SILC. */
806 SILC_CLIENT_CMD_FUNC(umode)
811 /* CMODE command. Sets channel mode. Modes that does not require any arguments
812 can be set several at once. Those modes that require argument must be set
813 separately (unless set with modes that does not require arguments). */
815 SILC_CLIENT_CMD_FUNC(cmode)
817 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
818 SilcClientConnection conn = cmd->conn;
819 SilcChannelEntry channel;
820 SilcBuffer buffer, chidp;
821 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
822 unsigned int mode, add, type, len, arg_len = 0;
826 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
832 cmd->client->ops->say(cmd->client, conn,
833 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
838 if (cmd->argv[1][0] == '*') {
839 if (!conn->current_channel) {
840 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
845 channel = conn->current_channel;
849 channel = silc_idlist_get_channel(cmd->client, conn, name);
851 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
857 mode = channel->mode;
859 /* Are we adding or removing mode */
860 if (cmd->argv[2][0] == '-')
865 /* Argument type to be sent to server */
869 cp = cmd->argv[2] + 1;
871 for (i = 0; i < len; i++) {
875 mode |= SILC_CHANNEL_MODE_PRIVATE;
877 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
881 mode |= SILC_CHANNEL_MODE_SECRET;
883 mode &= ~SILC_CHANNEL_MODE_SECRET;
887 mode |= SILC_CHANNEL_MODE_PRIVKEY;
889 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
893 mode |= SILC_CHANNEL_MODE_INVITE;
895 mode &= ~SILC_CHANNEL_MODE_INVITE;
899 mode |= SILC_CHANNEL_MODE_TOPIC;
901 mode &= ~SILC_CHANNEL_MODE_TOPIC;
906 mode |= SILC_CHANNEL_MODE_ULIMIT;
908 ll = atoi(cmd->argv[3]);
909 SILC_PUT32_MSB(ll, tmp);
913 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
918 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
921 arg_len = cmd->argv_lens[3];
923 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
928 mode |= SILC_CHANNEL_MODE_BAN;
931 arg_len = cmd->argv_lens[3];
933 mode &= ~SILC_CHANNEL_MODE_BAN;
938 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
941 arg_len = cmd->argv_lens[3];
943 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
948 mode |= SILC_CHANNEL_MODE_CIPHER;
951 arg_len = cmd->argv_lens[3];
953 mode &= ~SILC_CHANNEL_MODE_CIPHER;
963 if (type && cmd->argc < 3) {
968 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
969 SILC_PUT32_MSB(mode, modebuf);
971 /* Send the command packet. We support sending only one mode at once
972 that requires an argument. */
975 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
976 1, chidp->data, chidp->len,
977 2, modebuf, sizeof(modebuf),
981 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
982 1, chidp->data, chidp->len,
983 2, modebuf, sizeof(modebuf));
986 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
987 0, NULL, NULL, buffer->data, buffer->len, TRUE);
988 silc_buffer_free(buffer);
989 silc_buffer_free(chidp);
991 /* Notify application */
995 silc_client_command_free(cmd);
998 /* CUMODE command. Changes client's mode on a channel. */
1000 SILC_CLIENT_CMD_FUNC(cumode)
1002 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1003 SilcClientConnection conn = cmd->conn;
1004 SilcChannelEntry channel;
1005 SilcChannelUser chu;
1006 SilcClientEntry client_entry;
1007 SilcBuffer buffer, clidp, chidp;
1008 unsigned char *name, *cp, modebuf[4];
1009 unsigned int mode = 0, add, len;
1010 char *nickname = NULL, *server = NULL;
1011 unsigned int num = 0;
1015 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1020 if (cmd->argc < 4) {
1021 cmd->client->ops->say(cmd->client, conn,
1022 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1027 if (cmd->argv[1][0] == '*') {
1028 if (!conn->current_channel) {
1029 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1034 channel = conn->current_channel;
1036 name = cmd->argv[1];
1038 channel = silc_idlist_get_channel(cmd->client, conn, name);
1040 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1046 /* Parse the typed nickname. */
1047 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1048 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1053 /* Find client entry */
1054 client_entry = silc_idlist_get_client(cmd->client, conn,
1055 nickname, server, num, TRUE);
1056 if (!client_entry) {
1057 /* Client entry not found, it was requested thus mark this to be
1059 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1060 silc_client_command_destructor,
1061 silc_client_command_cumode,
1062 silc_client_command_dup(cmd));
1067 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1068 if (chu->client == client_entry) {
1074 /* Are we adding or removing mode */
1075 if (cmd->argv[2][0] == '-')
1081 cp = cmd->argv[2] + 1;
1083 for (i = 0; i < len; i++) {
1087 mode |= SILC_CHANNEL_UMODE_CHANFO;
1088 mode |= SILC_CHANNEL_UMODE_CHANOP;
1090 mode = SILC_CHANNEL_UMODE_NONE;
1095 mode |= SILC_CHANNEL_UMODE_CHANFO;
1097 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1101 mode |= SILC_CHANNEL_UMODE_CHANOP;
1103 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1112 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1113 SILC_PUT32_MSB(mode, modebuf);
1114 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1116 /* Send the command packet. We support sending only one mode at once
1117 that requires an argument. */
1118 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1119 1, chidp->data, chidp->len,
1121 3, clidp->data, clidp->len);
1123 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1124 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1125 silc_buffer_free(buffer);
1126 silc_buffer_free(chidp);
1127 silc_buffer_free(clidp);
1129 /* Notify application */
1133 silc_client_command_free(cmd);
1136 /* KICK command. Kicks a client out of channel. */
1138 SILC_CLIENT_CMD_FUNC(kick)
1140 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1141 SilcClientConnection conn = cmd->conn;
1142 SilcIDCacheEntry id_cache = NULL;
1143 SilcChannelEntry channel;
1144 SilcBuffer buffer, idp, idp2;
1145 SilcClientEntry target;
1147 unsigned int num = 0;
1148 char *nickname = NULL, *server = NULL;
1151 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1156 if (cmd->argc < 3) {
1157 cmd->client->ops->say(cmd->client, conn,
1158 "Usage: /KICK <channel> <client> [<comment>]");
1163 if (cmd->argv[1][0] == '*') {
1164 if (!conn->current_channel) {
1165 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1169 name = conn->current_channel->channel_name;
1171 name = cmd->argv[1];
1174 if (!conn->current_channel) {
1175 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1180 /* Get the Channel ID of the channel */
1181 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1182 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1187 channel = (SilcChannelEntry)id_cache->context;
1189 /* Parse the typed nickname. */
1190 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1191 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1196 /* Get the target client */
1197 target = silc_idlist_get_client(cmd->client, conn, nickname,
1198 server, num, FALSE);
1200 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1206 /* Send KICK command to the server */
1207 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1208 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1210 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1211 1, idp->data, idp->len,
1212 2, idp2->data, idp2->len);
1214 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1215 1, idp->data, idp->len,
1216 2, idp2->data, idp2->len,
1218 strlen(cmd->argv[3]));
1219 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1220 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1221 silc_buffer_free(buffer);
1222 silc_buffer_free(idp);
1223 silc_buffer_free(idp2);
1225 /* Notify application */
1229 silc_client_command_free(cmd);
1232 SILC_CLIENT_CMD_FUNC(restart)
1236 SILC_CLIENT_CMD_FUNC(close)
1240 SILC_CLIENT_CMD_FUNC(die)
1244 SILC_CLIENT_CMD_FUNC(silcoper)
1248 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1250 SILC_CLIENT_CMD_FUNC(leave)
1252 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1253 SilcClientConnection conn = cmd->conn;
1254 SilcIDCacheEntry id_cache = NULL;
1255 SilcChannelEntry channel;
1256 SilcBuffer buffer, idp;
1260 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1265 if (cmd->argc != 2) {
1266 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1271 if (cmd->argv[1][0] == '*') {
1272 if (!conn->current_channel) {
1273 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1277 name = conn->current_channel->channel_name;
1279 name = cmd->argv[1];
1282 if (!conn->current_channel) {
1283 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1288 /* Get the Channel ID of the channel */
1289 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1290 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1295 channel = (SilcChannelEntry)id_cache->context;
1297 /* Send LEAVE command to the server */
1298 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1299 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1300 1, idp->data, idp->len);
1301 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1302 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1303 silc_buffer_free(buffer);
1304 silc_buffer_free(idp);
1306 /* We won't talk anymore on this channel */
1307 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1309 conn->current_channel = NULL;
1311 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1312 silc_free(channel->channel_name);
1313 silc_free(channel->id);
1314 silc_free(channel->key);
1315 silc_cipher_free(channel->channel_key);
1318 /* Notify application */
1322 silc_client_command_free(cmd);
1325 /* Command USERS. Requests the USERS of the clients joined on requested
1328 SILC_CLIENT_CMD_FUNC(users)
1330 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1331 SilcClientConnection conn = cmd->conn;
1332 SilcIDCacheEntry id_cache = NULL;
1333 SilcChannelEntry channel;
1334 SilcBuffer buffer, idp;
1335 char *name, *line = NULL;
1336 unsigned int line_len = 0;
1339 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1344 if (cmd->argc != 2) {
1345 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1350 if (cmd->argv[1][0] == '*') {
1351 if (!conn->current_channel) {
1352 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1356 name = conn->current_channel->channel_name;
1358 name = cmd->argv[1];
1361 if (!conn->current_channel) {
1362 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1367 /* Get the Channel ID of the channel */
1368 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1369 /* XXX should resolve the channel ID; LIST command */
1370 cmd->client->ops->say(cmd->client, conn,
1371 "You are not on that channel", name);
1376 channel = (SilcChannelEntry)id_cache->context;
1378 if (!cmd->pending) {
1379 /* Send USERS command to the server */
1380 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1381 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1382 1, idp->data, idp->len);
1383 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1384 NULL, 0, NULL, NULL, buffer->data,
1386 silc_buffer_free(buffer);
1387 silc_buffer_free(idp);
1389 /* Register pending callback which will recall this command callback with
1390 same context and reprocesses the command. When reprocessing we actually
1391 display the information on the screen. */
1392 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1393 silc_client_command_destructor,
1394 silc_client_command_users,
1395 silc_client_command_dup(cmd));
1396 cmd->pending = TRUE;
1401 /* Pending command. Now we've resolved the information from server and
1402 we are ready to display the information on screen. */
1404 SilcChannelUser chu;
1406 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1407 channel->channel_name);
1409 line = silc_calloc(4096, sizeof(*line));
1411 silc_list_start(channel->clients);
1412 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1413 SilcClientEntry e = chu->client;
1414 char *m, tmp[80], len1;
1416 memset(line, 0, sizeof(line_len));
1418 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1420 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1421 line = silc_calloc(line_len, sizeof(*line));
1424 memset(tmp, 0, sizeof(tmp));
1425 m = silc_client_chumode_char(chu->mode);
1427 strncat(line, " ", 1);
1428 strncat(line, e->nickname, strlen(e->nickname));
1429 strncat(line, e->server ? "@" : "", 1);
1433 len1 = strlen(e->server);
1434 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1436 len1 = strlen(line);
1438 memset(&line[29], 0, len1 - 29);
1440 for (i = 0; i < 30 - len1 - 1; i++)
1444 strncat(line, " H", 3);
1445 strcat(tmp, m ? m : "");
1446 strncat(line, tmp, strlen(tmp));
1448 if (strlen(tmp) < 5)
1449 for (i = 0; i < 5 - strlen(tmp); i++)
1452 strcat(line, e->username ? e->username : "");
1454 cmd->client->ops->say(cmd->client, conn, "%s", line);
1464 /* Notify application */
1468 silc_client_command_free(cmd);