5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 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, 3),
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(shutdown, SHUTDOWN, "SHUTDOWN",
55 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
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 and 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 silc_client_command_dup(SilcClientCommandContext ctx)
207 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
212 /* Pending command destructor. */
214 static void silc_client_command_destructor(void *context)
216 silc_client_command_free((SilcClientCommandContext)context);
219 /* silc_client_get_client completion callback */
220 void silc_client_command_completion(SilcClient client,
221 SilcClientConnection conn,
222 SilcClientEntry clients,
223 unsigned int clients_count,
229 /* Command WHOIS. This command is used to query information about
232 SILC_CLIENT_CMD_FUNC(whois)
234 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
235 SilcClientConnection conn = cmd->conn;
239 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
244 if (cmd->argc < 2 || cmd->argc > 3) {
245 cmd->client->ops->say(cmd->client, conn,
246 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
251 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
252 cmd->argc - 1, ++cmd->argv,
253 ++cmd->argv_lens, ++cmd->argv_types,
255 silc_client_packet_send(cmd->client, cmd->conn->sock,
256 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
257 buffer->data, buffer->len, TRUE);
258 silc_buffer_free(buffer);
263 /* Notify application */
267 silc_client_command_free(cmd);
270 SILC_CLIENT_CMD_FUNC(whowas)
274 /* Command IDENTIFY. This command is used to query information about
275 specific user, especially ID's. */
277 SILC_CLIENT_CMD_FUNC(identify)
279 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
280 SilcClientConnection conn = cmd->conn;
284 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
289 if (cmd->argc < 2 || cmd->argc > 3) {
290 cmd->client->ops->say(cmd->client, conn,
291 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
296 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
297 cmd->argc - 1, ++cmd->argv,
298 ++cmd->argv_lens, ++cmd->argv_types,
300 silc_client_packet_send(cmd->client, cmd->conn->sock,
301 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
302 buffer->data, buffer->len, TRUE);
303 silc_buffer_free(buffer);
308 /* Notify application */
312 silc_client_command_free(cmd);
315 /* Command NICK. Shows current nickname/sets new nickname on current
318 SILC_CLIENT_CMD_FUNC(nick)
320 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
321 SilcClientConnection conn = cmd->conn;
325 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
330 if (!strcmp(conn->nickname, cmd->argv[1]))
333 /* Show current nickname */
336 cmd->client->ops->say(cmd->client, conn,
337 "Your nickname is %s on server %s",
338 conn->nickname, conn->remote_host);
340 cmd->client->ops->say(cmd->client, conn,
341 "Your nickname is %s", conn->nickname);
344 /* XXX Notify application */
349 /* Set new nickname */
350 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
351 cmd->argc - 1, ++cmd->argv,
352 ++cmd->argv_lens, ++cmd->argv_types,
354 silc_client_packet_send(cmd->client, cmd->conn->sock,
355 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
356 buffer->data, buffer->len, TRUE);
357 silc_buffer_free(buffer);
362 silc_free(conn->nickname);
363 conn->nickname = strdup(cmd->argv[1]);
365 /* Notify application */
369 silc_client_command_free(cmd);
372 SILC_CLIENT_CMD_FUNC(list)
376 /* Command TOPIC. Sets/shows topic on a channel. */
378 SILC_CLIENT_CMD_FUNC(topic)
380 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
381 SilcClientConnection conn = cmd->conn;
382 SilcIDCacheEntry id_cache = NULL;
383 SilcChannelEntry channel;
384 SilcBuffer buffer, idp;
388 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
393 if (cmd->argc < 2 || cmd->argc > 3) {
394 cmd->client->ops->say(cmd->client, conn,
395 "Usage: /TOPIC <channel> [<topic>]");
400 if (cmd->argv[1][0] == '*') {
401 if (!conn->current_channel) {
402 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
406 name = conn->current_channel->channel_name;
411 if (!conn->current_channel) {
412 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
417 /* Get the Channel ID of the channel */
418 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
419 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
424 channel = (SilcChannelEntry)id_cache->context;
426 /* Send TOPIC command to the server */
427 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
429 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
430 1, idp->data, idp->len,
432 strlen(cmd->argv[2]));
434 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
435 1, idp->data, idp->len,
437 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
438 0, NULL, NULL, buffer->data, buffer->len, TRUE);
439 silc_buffer_free(buffer);
440 silc_buffer_free(idp);
442 /* Notify application */
446 silc_client_command_free(cmd);
449 /* Command INVITE. Invites specific client to join a channel. */
451 SILC_CLIENT_CMD_FUNC(invite)
453 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
454 SilcClient client = cmd->client;
455 SilcClientConnection conn = cmd->conn;
456 SilcClientEntry client_entry;
457 SilcChannelEntry channel_entry;
458 SilcBuffer buffer, clidp, chidp;
459 unsigned int num = 0;
460 char *nickname = NULL, *server = NULL;
463 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
468 if (cmd->argc != 3) {
469 cmd->client->ops->say(cmd->client, conn,
470 "Usage: /INVITE <nickname>[@<server>] <channel>");
475 /* Parse the typed nickname. */
476 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
477 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
482 /* Find client entry */
483 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
491 /* Client entry not found, it was requested thus mark this to be
493 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
494 silc_client_command_destructor,
495 silc_client_command_invite,
496 silc_client_command_dup(cmd));
501 /* Find channel entry */
502 channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
503 if (!channel_entry) {
504 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
510 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
511 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
512 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
513 1, clidp->data, clidp->len,
514 2, chidp->data, chidp->len);
515 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
516 0, NULL, NULL, buffer->data, buffer->len, TRUE);
517 silc_buffer_free(buffer);
518 silc_buffer_free(clidp);
519 silc_buffer_free(chidp);
521 cmd->client->ops->say(cmd->client, conn,
522 "Inviting %s to channel %s", cmd->argv[1],
525 /* Notify application */
529 silc_client_command_free(cmd);
534 SilcClientConnection conn;
537 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
539 QuitInternal q = (QuitInternal)context;
541 /* Close connection */
542 q->client->ops->disconnect(q->client, q->conn);
543 silc_client_close_connection(q->client, q->conn->sock->user_data);
548 /* Command QUIT. Closes connection with current server. */
550 SILC_CLIENT_CMD_FUNC(quit)
552 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
557 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
563 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
564 &cmd->argv[1], &cmd->argv_lens[1],
565 &cmd->argv_types[1], 0);
567 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
568 NULL, NULL, NULL, 0);
569 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
571 buffer->data, buffer->len, TRUE);
572 silc_buffer_free(buffer);
574 q = silc_calloc(1, sizeof(*q));
575 q->client = cmd->client;
578 /* We quit the connection with little timeout */
579 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
580 silc_client_command_quit_cb, (void *)q,
581 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
583 /* Notify application */
587 silc_client_command_free(cmd);
590 SILC_CLIENT_CMD_FUNC(kill)
594 /* Command INFO. Request information about specific server. If specific
595 server is not provided the current server is used. */
597 SILC_CLIENT_CMD_FUNC(info)
599 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
600 SilcClientConnection conn = cmd->conn;
605 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
611 name = strdup(conn->remote_host);
613 name = strdup(cmd->argv[1]);
615 /* Send the command */
616 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
617 1, name, strlen(name));
618 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
619 0, NULL, NULL, buffer->data, buffer->len, TRUE);
620 silc_buffer_free(buffer);
622 /* Notify application */
626 silc_client_command_free(cmd);
629 /* Command PING. Sends ping to server. This is used to test the
630 communication channel. */
632 SILC_CLIENT_CMD_FUNC(ping)
634 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
635 SilcClientConnection conn = cmd->conn;
642 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
647 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
648 name = strdup(conn->remote_host);
650 /* Send the command */
651 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
652 1, conn->remote_id_data,
654 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
655 0, NULL, NULL, buffer->data, buffer->len, TRUE);
656 silc_buffer_free(buffer);
658 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
661 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
666 /* Start counting time */
667 for (i = 0; i < conn->ping_count; i++) {
668 if (conn->ping[i].dest_id == NULL) {
669 conn->ping[i].start_time = time(NULL);
670 conn->ping[i].dest_id = id;
671 conn->ping[i].dest_name = name;
676 if (i >= conn->ping_count) {
677 i = conn->ping_count;
678 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
679 conn->ping[i].start_time = time(NULL);
680 conn->ping[i].dest_id = id;
681 conn->ping[i].dest_name = name;
685 /* Notify application */
689 silc_client_command_free(cmd);
692 SILC_CLIENT_CMD_FUNC(notice)
696 /* Command JOIN. Joins to a channel. */
698 SILC_CLIENT_CMD_FUNC(join)
700 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
701 SilcClientConnection conn = cmd->conn;
702 SilcIDCacheEntry id_cache = NULL;
703 SilcBuffer buffer, idp;
706 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
712 /* Show channels currently joined to */
717 /* See if we have joined to the requested channel already */
718 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
720 cmd->client->ops->say(cmd->client, conn,
721 "You are talking to channel %s", cmd->argv[1]);
722 conn->current_channel = (SilcChannelEntry)id_cache->context;
724 cmd->client->screen->bottom_line->channel = cmd->argv[1];
725 silc_screen_print_bottom_line(cmd->client->screen, 0);
730 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
732 /* Send JOIN command to the server */
735 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
736 1, cmd->argv[1], cmd->argv_lens[1],
737 2, idp->data, idp->len);
738 else if (cmd->argc == 3)
741 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
742 1, cmd->argv[1], cmd->argv_lens[1],
743 2, idp->data, idp->len,
744 3, cmd->argv[2], cmd->argv_lens[2]);
747 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
748 1, cmd->argv[1], cmd->argv_lens[1],
749 2, idp->data, idp->len,
750 3, cmd->argv[2], cmd->argv_lens[2],
751 4, cmd->argv[3], cmd->argv_lens[3]);
753 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
754 0, NULL, NULL, buffer->data, buffer->len, TRUE);
755 silc_buffer_free(buffer);
756 silc_buffer_free(idp);
758 /* Notify application */
762 silc_client_command_free(cmd);
765 /* MOTD command. Requests motd from server. */
767 SILC_CLIENT_CMD_FUNC(motd)
769 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
770 SilcClientConnection conn = cmd->conn;
774 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
779 if (cmd->argc < 1 || cmd->argc > 1) {
780 cmd->client->ops->say(cmd->client, conn,
786 /* Send TOPIC command to the server */
787 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
788 2, conn->remote_host,
789 strlen(conn->remote_host));
790 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
791 0, NULL, NULL, buffer->data, buffer->len, TRUE);
792 silc_buffer_free(buffer);
794 /* Notify application */
798 silc_client_command_free(cmd);
801 /* UMODE. Set user mode in SILC. */
803 SILC_CLIENT_CMD_FUNC(umode)
808 /* CMODE command. Sets channel mode. Modes that does not require any arguments
809 can be set several at once. Those modes that require argument must be set
810 separately (unless set with modes that does not require arguments). */
812 SILC_CLIENT_CMD_FUNC(cmode)
814 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
815 SilcClientConnection conn = cmd->conn;
816 SilcChannelEntry channel;
817 SilcBuffer buffer, chidp;
818 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
819 unsigned int mode, add, type, len, arg_len = 0;
823 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
829 cmd->client->ops->say(cmd->client, conn,
830 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
835 if (cmd->argv[1][0] == '*') {
836 if (!conn->current_channel) {
837 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
842 channel = conn->current_channel;
846 channel = silc_client_get_channel(cmd->client, conn, name);
848 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
854 mode = channel->mode;
856 /* Are we adding or removing mode */
857 if (cmd->argv[2][0] == '-')
862 /* Argument type to be sent to server */
866 cp = cmd->argv[2] + 1;
868 for (i = 0; i < len; i++) {
872 mode |= SILC_CHANNEL_MODE_PRIVATE;
874 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
878 mode |= SILC_CHANNEL_MODE_SECRET;
880 mode &= ~SILC_CHANNEL_MODE_SECRET;
884 mode |= SILC_CHANNEL_MODE_PRIVKEY;
886 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
890 mode |= SILC_CHANNEL_MODE_INVITE;
892 mode &= ~SILC_CHANNEL_MODE_INVITE;
896 mode |= SILC_CHANNEL_MODE_TOPIC;
898 mode &= ~SILC_CHANNEL_MODE_TOPIC;
903 mode |= SILC_CHANNEL_MODE_ULIMIT;
905 ll = atoi(cmd->argv[3]);
906 SILC_PUT32_MSB(ll, tmp);
910 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
915 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
918 arg_len = cmd->argv_lens[3];
920 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
925 mode |= SILC_CHANNEL_MODE_BAN;
928 arg_len = cmd->argv_lens[3];
930 mode &= ~SILC_CHANNEL_MODE_BAN;
935 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
938 arg_len = cmd->argv_lens[3];
940 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
945 mode |= SILC_CHANNEL_MODE_CIPHER;
948 arg_len = cmd->argv_lens[3];
950 mode &= ~SILC_CHANNEL_MODE_CIPHER;
960 if (type && cmd->argc < 3) {
965 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
966 SILC_PUT32_MSB(mode, modebuf);
968 /* Send the command packet. We support sending only one mode at once
969 that requires an argument. */
972 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
973 1, chidp->data, chidp->len,
974 2, modebuf, sizeof(modebuf),
978 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
979 1, chidp->data, chidp->len,
980 2, modebuf, sizeof(modebuf));
983 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
984 0, NULL, NULL, buffer->data, buffer->len, TRUE);
985 silc_buffer_free(buffer);
986 silc_buffer_free(chidp);
988 /* Notify application */
992 silc_client_command_free(cmd);
995 /* CUMODE command. Changes client's mode on a channel. */
997 SILC_CLIENT_CMD_FUNC(cumode)
999 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1000 SilcClientConnection conn = cmd->conn;
1001 SilcChannelEntry channel;
1002 SilcChannelUser chu;
1003 SilcClientEntry client_entry;
1004 SilcBuffer buffer, clidp, chidp;
1005 unsigned char *name, *cp, modebuf[4];
1006 unsigned int mode = 0, add, len;
1007 char *nickname = NULL, *server = NULL;
1008 unsigned int num = 0;
1012 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1017 if (cmd->argc < 4) {
1018 cmd->client->ops->say(cmd->client, conn,
1019 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1024 if (cmd->argv[1][0] == '*') {
1025 if (!conn->current_channel) {
1026 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1031 channel = conn->current_channel;
1033 name = cmd->argv[1];
1035 channel = silc_client_get_channel(cmd->client, conn, name);
1037 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1043 /* Parse the typed nickname. */
1044 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1045 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1050 /* Find client entry */
1051 client_entry = silc_idlist_get_client(cmd->client, conn,
1052 nickname, server, num, TRUE);
1053 if (!client_entry) {
1054 /* Client entry not found, it was requested thus mark this to be
1056 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1057 silc_client_command_destructor,
1058 silc_client_command_cumode,
1059 silc_client_command_dup(cmd));
1064 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1065 if (chu->client == client_entry) {
1071 /* Are we adding or removing mode */
1072 if (cmd->argv[2][0] == '-')
1078 cp = cmd->argv[2] + 1;
1080 for (i = 0; i < len; i++) {
1084 mode |= SILC_CHANNEL_UMODE_CHANFO;
1085 mode |= SILC_CHANNEL_UMODE_CHANOP;
1087 mode = SILC_CHANNEL_UMODE_NONE;
1092 mode |= SILC_CHANNEL_UMODE_CHANFO;
1094 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1098 mode |= SILC_CHANNEL_UMODE_CHANOP;
1100 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1109 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1110 SILC_PUT32_MSB(mode, modebuf);
1111 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1113 /* Send the command packet. We support sending only one mode at once
1114 that requires an argument. */
1115 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1116 1, chidp->data, chidp->len,
1118 3, clidp->data, clidp->len);
1120 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1121 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1122 silc_buffer_free(buffer);
1123 silc_buffer_free(chidp);
1124 silc_buffer_free(clidp);
1126 /* Notify application */
1130 silc_client_command_free(cmd);
1133 /* KICK command. Kicks a client out of channel. */
1135 SILC_CLIENT_CMD_FUNC(kick)
1137 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1138 SilcClientConnection conn = cmd->conn;
1139 SilcIDCacheEntry id_cache = NULL;
1140 SilcChannelEntry channel;
1141 SilcBuffer buffer, idp, idp2;
1142 SilcClientEntry target;
1144 unsigned int num = 0;
1145 char *nickname = NULL, *server = NULL;
1148 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1153 if (cmd->argc < 3) {
1154 cmd->client->ops->say(cmd->client, conn,
1155 "Usage: /KICK <channel> <client> [<comment>]");
1160 if (cmd->argv[1][0] == '*') {
1161 if (!conn->current_channel) {
1162 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1166 name = conn->current_channel->channel_name;
1168 name = cmd->argv[1];
1171 if (!conn->current_channel) {
1172 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1177 /* Get the Channel ID of the channel */
1178 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1179 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1184 channel = (SilcChannelEntry)id_cache->context;
1186 /* Parse the typed nickname. */
1187 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1188 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1193 /* Get the target client */
1194 target = silc_idlist_get_client(cmd->client, conn, nickname,
1195 server, num, FALSE);
1197 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1203 /* Send KICK command to the server */
1204 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1205 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1207 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1208 1, idp->data, idp->len,
1209 2, idp2->data, idp2->len);
1211 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1212 1, idp->data, idp->len,
1213 2, idp2->data, idp2->len,
1215 strlen(cmd->argv[3]));
1216 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1217 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1218 silc_buffer_free(buffer);
1219 silc_buffer_free(idp);
1220 silc_buffer_free(idp2);
1222 /* Notify application */
1226 silc_client_command_free(cmd);
1229 SILC_CLIENT_CMD_FUNC(silcoper)
1233 SILC_CLIENT_CMD_FUNC(oper)
1237 /* CONNECT command. Connects the server to another server. */
1239 SILC_CLIENT_CMD_FUNC(connect)
1241 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1242 SilcClientConnection conn = cmd->conn;
1244 unsigned char port[4];
1248 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1253 if (cmd->argc < 2) {
1254 cmd->client->ops->say(cmd->client, conn,
1255 "Usage: /CONNECT <server> [<port>]");
1260 if (cmd->argc == 3) {
1261 tmp = atoi(cmd->argv[2]);
1262 SILC_PUT32_MSB(tmp, port);
1266 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1268 strlen(cmd->argv[1]),
1271 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1273 strlen(cmd->argv[1]));
1274 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1275 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1276 silc_buffer_free(buffer);
1278 /* Notify application */
1282 silc_client_command_free(cmd);
1285 SILC_CLIENT_CMD_FUNC(restart)
1289 /* CLOSE command. Close server connection to the remote server */
1291 SILC_CLIENT_CMD_FUNC(close)
1293 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1294 SilcClientConnection conn = cmd->conn;
1296 unsigned char port[4];
1300 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1305 if (cmd->argc < 2) {
1306 cmd->client->ops->say(cmd->client, conn,
1307 "Usage: /CLOSE <server> [<port>]");
1312 if (cmd->argc == 3) {
1313 tmp = atoi(cmd->argv[2]);
1314 SILC_PUT32_MSB(tmp, port);
1318 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1320 strlen(cmd->argv[1]),
1323 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1325 strlen(cmd->argv[1]));
1326 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1327 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1328 silc_buffer_free(buffer);
1330 /* Notify application */
1334 silc_client_command_free(cmd);
1337 /* SHUTDOWN command. Shutdowns the server. */
1339 SILC_CLIENT_CMD_FUNC(shutdown)
1341 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1344 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1349 /* Send the command */
1350 silc_client_send_command(cmd->client, cmd->conn,
1351 SILC_COMMAND_SHUTDOWN, 0, 0);
1353 /* Notify application */
1357 silc_client_command_free(cmd);
1360 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1362 SILC_CLIENT_CMD_FUNC(leave)
1364 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1365 SilcClientConnection conn = cmd->conn;
1366 SilcIDCacheEntry id_cache = NULL;
1367 SilcChannelEntry channel;
1368 SilcBuffer buffer, idp;
1372 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1377 if (cmd->argc != 2) {
1378 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1383 if (cmd->argv[1][0] == '*') {
1384 if (!conn->current_channel) {
1385 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1389 name = conn->current_channel->channel_name;
1391 name = cmd->argv[1];
1394 if (!conn->current_channel) {
1395 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1400 /* Get the Channel ID of the channel */
1401 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1402 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1407 channel = (SilcChannelEntry)id_cache->context;
1409 /* Send LEAVE command to the server */
1410 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1411 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1412 1, idp->data, idp->len);
1413 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1414 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1415 silc_buffer_free(buffer);
1416 silc_buffer_free(idp);
1418 /* We won't talk anymore on this channel */
1419 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1421 conn->current_channel = NULL;
1423 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1424 silc_free(channel->channel_name);
1425 silc_free(channel->id);
1426 silc_free(channel->key);
1427 silc_cipher_free(channel->channel_key);
1430 /* Notify application */
1434 silc_client_command_free(cmd);
1437 /* Command USERS. Requests the USERS of the clients joined on requested
1440 SILC_CLIENT_CMD_FUNC(users)
1442 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1443 SilcClientConnection conn = cmd->conn;
1444 SilcIDCacheEntry id_cache = NULL;
1445 SilcChannelEntry channel;
1446 SilcBuffer buffer, idp;
1447 char *name, *line = NULL;
1448 unsigned int line_len = 0;
1451 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1456 if (cmd->argc != 2) {
1457 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1462 if (cmd->argv[1][0] == '*') {
1463 if (!conn->current_channel) {
1464 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1468 name = conn->current_channel->channel_name;
1470 name = cmd->argv[1];
1473 if (!conn->current_channel) {
1474 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1479 /* Get the Channel ID of the channel */
1480 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1481 /* XXX should resolve the channel ID; LIST command */
1482 cmd->client->ops->say(cmd->client, conn,
1483 "You are not on that channel", name);
1488 channel = (SilcChannelEntry)id_cache->context;
1490 if (!cmd->pending) {
1491 /* Send USERS command to the server */
1492 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1493 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1494 1, idp->data, idp->len);
1495 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1496 NULL, 0, NULL, NULL, buffer->data,
1498 silc_buffer_free(buffer);
1499 silc_buffer_free(idp);
1501 /* Register pending callback which will recall this command callback with
1502 same context and reprocesses the command. When reprocessing we actually
1503 display the information on the screen. */
1504 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1505 silc_client_command_destructor,
1506 silc_client_command_users,
1507 silc_client_command_dup(cmd));
1508 cmd->pending = TRUE;
1513 /* Pending command. Now we've resolved the information from server and
1514 we are ready to display the information on screen. */
1516 SilcChannelUser chu;
1518 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1519 channel->channel_name);
1521 line = silc_calloc(4096, sizeof(*line));
1523 silc_list_start(channel->clients);
1524 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1525 SilcClientEntry e = chu->client;
1526 char *m, tmp[80], len1;
1528 memset(line, 0, sizeof(line_len));
1530 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1532 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1533 line = silc_calloc(line_len, sizeof(*line));
1536 memset(tmp, 0, sizeof(tmp));
1537 m = silc_client_chumode_char(chu->mode);
1539 strncat(line, " ", 1);
1540 strncat(line, e->nickname, strlen(e->nickname));
1541 strncat(line, e->server ? "@" : "", 1);
1545 len1 = strlen(e->server);
1546 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1548 len1 = strlen(line);
1550 memset(&line[29], 0, len1 - 29);
1552 for (i = 0; i < 30 - len1 - 1; i++)
1556 strncat(line, " H", 3);
1557 strcat(tmp, m ? m : "");
1558 strncat(line, tmp, strlen(tmp));
1560 if (strlen(tmp) < 5)
1561 for (i = 0; i < 5 - strlen(tmp); i++)
1564 strcat(line, e->username ? e->username : "");
1566 cmd->client->ops->say(cmd->client, conn, "%s", line);
1576 /* Notify application */
1580 silc_client_command_free(cmd);