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, 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 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 /* Command PING. Sends ping to server. This is used to test the
621 communication channel. */
623 SILC_CLIENT_CMD_FUNC(ping)
625 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
626 SilcClientConnection conn = cmd->conn;
633 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
638 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
639 name = strdup(conn->remote_host);
641 /* Send the command */
642 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
643 1, conn->remote_id_data,
645 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
646 0, NULL, NULL, buffer->data, buffer->len, TRUE);
647 silc_buffer_free(buffer);
649 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
652 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
657 /* Start counting time */
658 for (i = 0; i < conn->ping_count; i++) {
659 if (conn->ping[i].dest_id == NULL) {
660 conn->ping[i].start_time = time(NULL);
661 conn->ping[i].dest_id = id;
662 conn->ping[i].dest_name = name;
667 if (i >= conn->ping_count) {
668 i = conn->ping_count;
669 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
670 conn->ping[i].start_time = time(NULL);
671 conn->ping[i].dest_id = id;
672 conn->ping[i].dest_name = name;
676 /* Notify application */
680 silc_client_command_free(cmd);
683 SILC_CLIENT_CMD_FUNC(trace)
687 SILC_CLIENT_CMD_FUNC(notice)
691 /* Command JOIN. Joins to a channel. */
693 SILC_CLIENT_CMD_FUNC(join)
695 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
696 SilcClientConnection conn = cmd->conn;
697 SilcIDCacheEntry id_cache = NULL;
698 SilcBuffer buffer, idp;
701 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
707 /* Show channels currently joined to */
712 /* See if we have joined to the requested channel already */
713 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
715 cmd->client->ops->say(cmd->client, conn,
716 "You are talking to channel %s", cmd->argv[1]);
717 conn->current_channel = (SilcChannelEntry)id_cache->context;
719 cmd->client->screen->bottom_line->channel = cmd->argv[1];
720 silc_screen_print_bottom_line(cmd->client->screen, 0);
725 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
727 /* Send JOIN command to the server */
730 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
731 1, cmd->argv[1], cmd->argv_lens[1],
732 2, idp->data, idp->len);
733 else if (cmd->argc == 3)
736 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
737 1, cmd->argv[1], cmd->argv_lens[1],
738 2, idp->data, idp->len,
739 3, cmd->argv[2], cmd->argv_lens[2]);
742 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
743 1, cmd->argv[1], cmd->argv_lens[1],
744 2, idp->data, idp->len,
745 3, cmd->argv[2], cmd->argv_lens[2],
746 4, cmd->argv[3], cmd->argv_lens[3]);
748 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
749 0, NULL, NULL, buffer->data, buffer->len, TRUE);
750 silc_buffer_free(buffer);
751 silc_buffer_free(idp);
753 /* Notify application */
757 silc_client_command_free(cmd);
760 /* MOTD command. Requests motd from server. */
762 SILC_CLIENT_CMD_FUNC(motd)
764 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
765 SilcClientConnection conn = cmd->conn;
769 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
774 if (cmd->argc < 1 || cmd->argc > 1) {
775 cmd->client->ops->say(cmd->client, conn,
781 /* Send TOPIC command to the server */
782 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
783 2, conn->remote_host,
784 strlen(conn->remote_host));
785 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
786 0, NULL, NULL, buffer->data, buffer->len, TRUE);
787 silc_buffer_free(buffer);
789 /* Notify application */
793 silc_client_command_free(cmd);
796 /* UMODE. Set user mode in SILC. */
798 SILC_CLIENT_CMD_FUNC(umode)
803 /* CMODE command. Sets channel mode. Modes that does not require any arguments
804 can be set several at once. Those modes that require argument must be set
805 separately (unless set with modes that does not require arguments). */
807 SILC_CLIENT_CMD_FUNC(cmode)
809 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
810 SilcClientConnection conn = cmd->conn;
811 SilcChannelEntry channel;
812 SilcBuffer buffer, chidp;
813 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
814 unsigned int mode, add, type, len, arg_len = 0;
818 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
824 cmd->client->ops->say(cmd->client, conn,
825 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
830 if (cmd->argv[1][0] == '*') {
831 if (!conn->current_channel) {
832 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
837 channel = conn->current_channel;
841 channel = silc_idlist_get_channel(cmd->client, conn, name);
843 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
849 mode = channel->mode;
851 /* Are we adding or removing mode */
852 if (cmd->argv[2][0] == '-')
857 /* Argument type to be sent to server */
861 cp = cmd->argv[2] + 1;
863 for (i = 0; i < len; i++) {
867 mode |= SILC_CHANNEL_MODE_PRIVATE;
869 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
873 mode |= SILC_CHANNEL_MODE_SECRET;
875 mode &= ~SILC_CHANNEL_MODE_SECRET;
879 mode |= SILC_CHANNEL_MODE_PRIVKEY;
881 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
885 mode |= SILC_CHANNEL_MODE_INVITE;
887 mode &= ~SILC_CHANNEL_MODE_INVITE;
891 mode |= SILC_CHANNEL_MODE_TOPIC;
893 mode &= ~SILC_CHANNEL_MODE_TOPIC;
898 mode |= SILC_CHANNEL_MODE_ULIMIT;
900 ll = atoi(cmd->argv[3]);
901 SILC_PUT32_MSB(ll, tmp);
905 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
910 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
913 arg_len = cmd->argv_lens[3];
915 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
920 mode |= SILC_CHANNEL_MODE_BAN;
923 arg_len = cmd->argv_lens[3];
925 mode &= ~SILC_CHANNEL_MODE_BAN;
930 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
933 arg_len = cmd->argv_lens[3];
935 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
940 mode |= SILC_CHANNEL_MODE_CIPHER;
943 arg_len = cmd->argv_lens[3];
945 mode &= ~SILC_CHANNEL_MODE_CIPHER;
955 if (type && cmd->argc < 3) {
960 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
961 SILC_PUT32_MSB(mode, modebuf);
963 /* Send the command packet. We support sending only one mode at once
964 that requires an argument. */
967 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
968 1, chidp->data, chidp->len,
969 2, modebuf, sizeof(modebuf),
973 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
974 1, chidp->data, chidp->len,
975 2, modebuf, sizeof(modebuf));
978 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
979 0, NULL, NULL, buffer->data, buffer->len, TRUE);
980 silc_buffer_free(buffer);
981 silc_buffer_free(chidp);
983 /* Notify application */
987 silc_client_command_free(cmd);
990 /* CUMODE command. Changes client's mode on a channel. */
992 SILC_CLIENT_CMD_FUNC(cumode)
994 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
995 SilcClientConnection conn = cmd->conn;
996 SilcChannelEntry channel;
998 SilcClientEntry client_entry;
999 SilcBuffer buffer, clidp, chidp;
1000 unsigned char *name, *cp, modebuf[4];
1001 unsigned int mode = 0, add, len;
1002 char *nickname = NULL, *server = NULL;
1003 unsigned int num = 0;
1007 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1012 if (cmd->argc < 4) {
1013 cmd->client->ops->say(cmd->client, conn,
1014 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1019 if (cmd->argv[1][0] == '*') {
1020 if (!conn->current_channel) {
1021 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1026 channel = conn->current_channel;
1028 name = cmd->argv[1];
1030 channel = silc_idlist_get_channel(cmd->client, conn, name);
1032 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1038 /* Parse the typed nickname. */
1039 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1040 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1045 /* Find client entry */
1046 client_entry = silc_idlist_get_client(cmd->client, conn,
1047 nickname, server, num, TRUE);
1048 if (!client_entry) {
1049 /* Client entry not found, it was requested thus mark this to be
1051 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1052 silc_client_command_destructor,
1053 silc_client_command_cumode,
1054 silc_client_command_dup(cmd));
1059 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1060 if (chu->client == client_entry) {
1066 /* Are we adding or removing mode */
1067 if (cmd->argv[2][0] == '-')
1073 cp = cmd->argv[2] + 1;
1075 for (i = 0; i < len; i++) {
1079 mode |= SILC_CHANNEL_UMODE_CHANFO;
1080 mode |= SILC_CHANNEL_UMODE_CHANOP;
1082 mode = SILC_CHANNEL_UMODE_NONE;
1087 mode |= SILC_CHANNEL_UMODE_CHANFO;
1089 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1093 mode |= SILC_CHANNEL_UMODE_CHANOP;
1095 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1104 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1105 SILC_PUT32_MSB(mode, modebuf);
1106 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1108 /* Send the command packet. We support sending only one mode at once
1109 that requires an argument. */
1110 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1111 1, chidp->data, chidp->len,
1113 3, clidp->data, clidp->len);
1115 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1116 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1117 silc_buffer_free(buffer);
1118 silc_buffer_free(chidp);
1119 silc_buffer_free(clidp);
1121 /* Notify application */
1125 silc_client_command_free(cmd);
1128 /* KICK command. Kicks a client out of channel. */
1130 SILC_CLIENT_CMD_FUNC(kick)
1132 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1133 SilcClientConnection conn = cmd->conn;
1134 SilcIDCacheEntry id_cache = NULL;
1135 SilcChannelEntry channel;
1136 SilcBuffer buffer, idp, idp2;
1137 SilcClientEntry target;
1139 unsigned int num = 0;
1140 char *nickname = NULL, *server = NULL;
1143 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1148 if (cmd->argc < 3) {
1149 cmd->client->ops->say(cmd->client, conn,
1150 "Usage: /KICK <channel> <client> [<comment>]");
1155 if (cmd->argv[1][0] == '*') {
1156 if (!conn->current_channel) {
1157 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1161 name = conn->current_channel->channel_name;
1163 name = cmd->argv[1];
1166 if (!conn->current_channel) {
1167 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1172 /* Get the Channel ID of the channel */
1173 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1174 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1179 channel = (SilcChannelEntry)id_cache->context;
1181 /* Parse the typed nickname. */
1182 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1183 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1188 /* Get the target client */
1189 target = silc_idlist_get_client(cmd->client, conn, nickname,
1190 server, num, FALSE);
1192 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1198 /* Send KICK command to the server */
1199 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1200 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1202 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1203 1, idp->data, idp->len,
1204 2, idp2->data, idp2->len);
1206 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1207 1, idp->data, idp->len,
1208 2, idp2->data, idp2->len,
1210 strlen(cmd->argv[3]));
1211 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1212 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1213 silc_buffer_free(buffer);
1214 silc_buffer_free(idp);
1215 silc_buffer_free(idp2);
1217 /* Notify application */
1221 silc_client_command_free(cmd);
1224 SILC_CLIENT_CMD_FUNC(silcoper)
1228 SILC_CLIENT_CMD_FUNC(oper)
1232 /* CONNECT command. Connects the server to another server. */
1234 SILC_CLIENT_CMD_FUNC(connect)
1236 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1237 SilcClientConnection conn = cmd->conn;
1239 unsigned char port[4];
1242 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1247 if (cmd->argc < 2) {
1248 cmd->client->ops->say(cmd->client, conn,
1249 "Usage: /CONNECT <server> [<port>]");
1255 SILC_PUT32_MSB(atoi(cmd->argv[2]), port);
1258 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1260 strlen(cmd->argv[1]),
1263 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1265 strlen(cmd->argv[1]));
1266 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1267 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1268 silc_buffer_free(buffer);
1270 /* Notify application */
1274 silc_client_command_free(cmd);
1277 SILC_CLIENT_CMD_FUNC(restart)
1281 SILC_CLIENT_CMD_FUNC(close)
1285 /* SHUTDOWN command. Shutdowns the server. */
1287 SILC_CLIENT_CMD_FUNC(shutdown)
1289 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1292 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1297 /* Send the command */
1298 silc_client_send_command(cmd->client, cmd->conn,
1299 SILC_COMMAND_SHUTDOWN, 0, 0);
1301 /* Notify application */
1305 silc_client_command_free(cmd);
1308 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1310 SILC_CLIENT_CMD_FUNC(leave)
1312 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1313 SilcClientConnection conn = cmd->conn;
1314 SilcIDCacheEntry id_cache = NULL;
1315 SilcChannelEntry channel;
1316 SilcBuffer buffer, idp;
1320 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1325 if (cmd->argc != 2) {
1326 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1331 if (cmd->argv[1][0] == '*') {
1332 if (!conn->current_channel) {
1333 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1337 name = conn->current_channel->channel_name;
1339 name = cmd->argv[1];
1342 if (!conn->current_channel) {
1343 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1348 /* Get the Channel ID of the channel */
1349 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1350 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1355 channel = (SilcChannelEntry)id_cache->context;
1357 /* Send LEAVE command to the server */
1358 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1359 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1360 1, idp->data, idp->len);
1361 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1362 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1363 silc_buffer_free(buffer);
1364 silc_buffer_free(idp);
1366 /* We won't talk anymore on this channel */
1367 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1369 conn->current_channel = NULL;
1371 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1372 silc_free(channel->channel_name);
1373 silc_free(channel->id);
1374 silc_free(channel->key);
1375 silc_cipher_free(channel->channel_key);
1378 /* Notify application */
1382 silc_client_command_free(cmd);
1385 /* Command USERS. Requests the USERS of the clients joined on requested
1388 SILC_CLIENT_CMD_FUNC(users)
1390 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1391 SilcClientConnection conn = cmd->conn;
1392 SilcIDCacheEntry id_cache = NULL;
1393 SilcChannelEntry channel;
1394 SilcBuffer buffer, idp;
1395 char *name, *line = NULL;
1396 unsigned int line_len = 0;
1399 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1404 if (cmd->argc != 2) {
1405 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1410 if (cmd->argv[1][0] == '*') {
1411 if (!conn->current_channel) {
1412 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1416 name = conn->current_channel->channel_name;
1418 name = cmd->argv[1];
1421 if (!conn->current_channel) {
1422 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1427 /* Get the Channel ID of the channel */
1428 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1429 /* XXX should resolve the channel ID; LIST command */
1430 cmd->client->ops->say(cmd->client, conn,
1431 "You are not on that channel", name);
1436 channel = (SilcChannelEntry)id_cache->context;
1438 if (!cmd->pending) {
1439 /* Send USERS command to the server */
1440 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1441 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1442 1, idp->data, idp->len);
1443 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1444 NULL, 0, NULL, NULL, buffer->data,
1446 silc_buffer_free(buffer);
1447 silc_buffer_free(idp);
1449 /* Register pending callback which will recall this command callback with
1450 same context and reprocesses the command. When reprocessing we actually
1451 display the information on the screen. */
1452 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1453 silc_client_command_destructor,
1454 silc_client_command_users,
1455 silc_client_command_dup(cmd));
1456 cmd->pending = TRUE;
1461 /* Pending command. Now we've resolved the information from server and
1462 we are ready to display the information on screen. */
1464 SilcChannelUser chu;
1466 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1467 channel->channel_name);
1469 line = silc_calloc(4096, sizeof(*line));
1471 silc_list_start(channel->clients);
1472 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1473 SilcClientEntry e = chu->client;
1474 char *m, tmp[80], len1;
1476 memset(line, 0, sizeof(line_len));
1478 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1480 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1481 line = silc_calloc(line_len, sizeof(*line));
1484 memset(tmp, 0, sizeof(tmp));
1485 m = silc_client_chumode_char(chu->mode);
1487 strncat(line, " ", 1);
1488 strncat(line, e->nickname, strlen(e->nickname));
1489 strncat(line, e->server ? "@" : "", 1);
1493 len1 = strlen(e->server);
1494 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1496 len1 = strlen(line);
1498 memset(&line[29], 0, len1 - 29);
1500 for (i = 0; i < 30 - len1 - 1; i++)
1504 strncat(line, " H", 3);
1505 strcat(tmp, m ? m : "");
1506 strncat(line, tmp, strlen(tmp));
1508 if (strlen(tmp) < 5)
1509 for (i = 0; i < 5 - strlen(tmp); i++)
1512 strcat(line, e->username ? e->username : "");
1514 cmd->client->ops->say(cmd->client, conn, "%s", line);
1524 /* Notify application */
1528 silc_client_command_free(cmd);