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(notice)
687 /* Command JOIN. Joins to a channel. */
689 SILC_CLIENT_CMD_FUNC(join)
691 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
692 SilcClientConnection conn = cmd->conn;
693 SilcIDCacheEntry id_cache = NULL;
694 SilcBuffer buffer, idp;
697 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
703 /* Show channels currently joined to */
708 /* See if we have joined to the requested channel already */
709 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
711 cmd->client->ops->say(cmd->client, conn,
712 "You are talking to channel %s", cmd->argv[1]);
713 conn->current_channel = (SilcChannelEntry)id_cache->context;
715 cmd->client->screen->bottom_line->channel = cmd->argv[1];
716 silc_screen_print_bottom_line(cmd->client->screen, 0);
721 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
723 /* Send JOIN command to the server */
726 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
727 1, cmd->argv[1], cmd->argv_lens[1],
728 2, idp->data, idp->len);
729 else if (cmd->argc == 3)
732 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
733 1, cmd->argv[1], cmd->argv_lens[1],
734 2, idp->data, idp->len,
735 3, cmd->argv[2], cmd->argv_lens[2]);
738 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
739 1, cmd->argv[1], cmd->argv_lens[1],
740 2, idp->data, idp->len,
741 3, cmd->argv[2], cmd->argv_lens[2],
742 4, cmd->argv[3], cmd->argv_lens[3]);
744 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
745 0, NULL, NULL, buffer->data, buffer->len, TRUE);
746 silc_buffer_free(buffer);
747 silc_buffer_free(idp);
749 /* Notify application */
753 silc_client_command_free(cmd);
756 /* MOTD command. Requests motd from server. */
758 SILC_CLIENT_CMD_FUNC(motd)
760 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
761 SilcClientConnection conn = cmd->conn;
765 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
770 if (cmd->argc < 1 || cmd->argc > 1) {
771 cmd->client->ops->say(cmd->client, conn,
777 /* Send TOPIC command to the server */
778 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
779 2, conn->remote_host,
780 strlen(conn->remote_host));
781 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
782 0, NULL, NULL, buffer->data, buffer->len, TRUE);
783 silc_buffer_free(buffer);
785 /* Notify application */
789 silc_client_command_free(cmd);
792 /* UMODE. Set user mode in SILC. */
794 SILC_CLIENT_CMD_FUNC(umode)
799 /* CMODE command. Sets channel mode. Modes that does not require any arguments
800 can be set several at once. Those modes that require argument must be set
801 separately (unless set with modes that does not require arguments). */
803 SILC_CLIENT_CMD_FUNC(cmode)
805 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
806 SilcClientConnection conn = cmd->conn;
807 SilcChannelEntry channel;
808 SilcBuffer buffer, chidp;
809 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
810 unsigned int mode, add, type, len, arg_len = 0;
814 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
820 cmd->client->ops->say(cmd->client, conn,
821 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
826 if (cmd->argv[1][0] == '*') {
827 if (!conn->current_channel) {
828 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
833 channel = conn->current_channel;
837 channel = silc_idlist_get_channel(cmd->client, conn, name);
839 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
845 mode = channel->mode;
847 /* Are we adding or removing mode */
848 if (cmd->argv[2][0] == '-')
853 /* Argument type to be sent to server */
857 cp = cmd->argv[2] + 1;
859 for (i = 0; i < len; i++) {
863 mode |= SILC_CHANNEL_MODE_PRIVATE;
865 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
869 mode |= SILC_CHANNEL_MODE_SECRET;
871 mode &= ~SILC_CHANNEL_MODE_SECRET;
875 mode |= SILC_CHANNEL_MODE_PRIVKEY;
877 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
881 mode |= SILC_CHANNEL_MODE_INVITE;
883 mode &= ~SILC_CHANNEL_MODE_INVITE;
887 mode |= SILC_CHANNEL_MODE_TOPIC;
889 mode &= ~SILC_CHANNEL_MODE_TOPIC;
894 mode |= SILC_CHANNEL_MODE_ULIMIT;
896 ll = atoi(cmd->argv[3]);
897 SILC_PUT32_MSB(ll, tmp);
901 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
906 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
909 arg_len = cmd->argv_lens[3];
911 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
916 mode |= SILC_CHANNEL_MODE_BAN;
919 arg_len = cmd->argv_lens[3];
921 mode &= ~SILC_CHANNEL_MODE_BAN;
926 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
929 arg_len = cmd->argv_lens[3];
931 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
936 mode |= SILC_CHANNEL_MODE_CIPHER;
939 arg_len = cmd->argv_lens[3];
941 mode &= ~SILC_CHANNEL_MODE_CIPHER;
951 if (type && cmd->argc < 3) {
956 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
957 SILC_PUT32_MSB(mode, modebuf);
959 /* Send the command packet. We support sending only one mode at once
960 that requires an argument. */
963 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
964 1, chidp->data, chidp->len,
965 2, modebuf, sizeof(modebuf),
969 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
970 1, chidp->data, chidp->len,
971 2, modebuf, sizeof(modebuf));
974 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
975 0, NULL, NULL, buffer->data, buffer->len, TRUE);
976 silc_buffer_free(buffer);
977 silc_buffer_free(chidp);
979 /* Notify application */
983 silc_client_command_free(cmd);
986 /* CUMODE command. Changes client's mode on a channel. */
988 SILC_CLIENT_CMD_FUNC(cumode)
990 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
991 SilcClientConnection conn = cmd->conn;
992 SilcChannelEntry channel;
994 SilcClientEntry client_entry;
995 SilcBuffer buffer, clidp, chidp;
996 unsigned char *name, *cp, modebuf[4];
997 unsigned int mode = 0, add, len;
998 char *nickname = NULL, *server = NULL;
999 unsigned int num = 0;
1003 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1008 if (cmd->argc < 4) {
1009 cmd->client->ops->say(cmd->client, conn,
1010 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1015 if (cmd->argv[1][0] == '*') {
1016 if (!conn->current_channel) {
1017 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1022 channel = conn->current_channel;
1024 name = cmd->argv[1];
1026 channel = silc_idlist_get_channel(cmd->client, conn, name);
1028 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1034 /* Parse the typed nickname. */
1035 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1036 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1041 /* Find client entry */
1042 client_entry = silc_idlist_get_client(cmd->client, conn,
1043 nickname, server, num, TRUE);
1044 if (!client_entry) {
1045 /* Client entry not found, it was requested thus mark this to be
1047 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1048 silc_client_command_destructor,
1049 silc_client_command_cumode,
1050 silc_client_command_dup(cmd));
1055 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1056 if (chu->client == client_entry) {
1062 /* Are we adding or removing mode */
1063 if (cmd->argv[2][0] == '-')
1069 cp = cmd->argv[2] + 1;
1071 for (i = 0; i < len; i++) {
1075 mode |= SILC_CHANNEL_UMODE_CHANFO;
1076 mode |= SILC_CHANNEL_UMODE_CHANOP;
1078 mode = SILC_CHANNEL_UMODE_NONE;
1083 mode |= SILC_CHANNEL_UMODE_CHANFO;
1085 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1089 mode |= SILC_CHANNEL_UMODE_CHANOP;
1091 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1100 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1101 SILC_PUT32_MSB(mode, modebuf);
1102 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1104 /* Send the command packet. We support sending only one mode at once
1105 that requires an argument. */
1106 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1107 1, chidp->data, chidp->len,
1109 3, clidp->data, clidp->len);
1111 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1112 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1113 silc_buffer_free(buffer);
1114 silc_buffer_free(chidp);
1115 silc_buffer_free(clidp);
1117 /* Notify application */
1121 silc_client_command_free(cmd);
1124 /* KICK command. Kicks a client out of channel. */
1126 SILC_CLIENT_CMD_FUNC(kick)
1128 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1129 SilcClientConnection conn = cmd->conn;
1130 SilcIDCacheEntry id_cache = NULL;
1131 SilcChannelEntry channel;
1132 SilcBuffer buffer, idp, idp2;
1133 SilcClientEntry target;
1135 unsigned int num = 0;
1136 char *nickname = NULL, *server = NULL;
1139 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1144 if (cmd->argc < 3) {
1145 cmd->client->ops->say(cmd->client, conn,
1146 "Usage: /KICK <channel> <client> [<comment>]");
1151 if (cmd->argv[1][0] == '*') {
1152 if (!conn->current_channel) {
1153 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1157 name = conn->current_channel->channel_name;
1159 name = cmd->argv[1];
1162 if (!conn->current_channel) {
1163 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1168 /* Get the Channel ID of the channel */
1169 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1170 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1175 channel = (SilcChannelEntry)id_cache->context;
1177 /* Parse the typed nickname. */
1178 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1179 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1184 /* Get the target client */
1185 target = silc_idlist_get_client(cmd->client, conn, nickname,
1186 server, num, FALSE);
1188 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1194 /* Send KICK command to the server */
1195 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1196 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1198 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1199 1, idp->data, idp->len,
1200 2, idp2->data, idp2->len);
1202 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1203 1, idp->data, idp->len,
1204 2, idp2->data, idp2->len,
1206 strlen(cmd->argv[3]));
1207 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1208 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1209 silc_buffer_free(buffer);
1210 silc_buffer_free(idp);
1211 silc_buffer_free(idp2);
1213 /* Notify application */
1217 silc_client_command_free(cmd);
1220 SILC_CLIENT_CMD_FUNC(silcoper)
1224 SILC_CLIENT_CMD_FUNC(oper)
1228 /* CONNECT command. Connects the server to another server. */
1230 SILC_CLIENT_CMD_FUNC(connect)
1232 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1233 SilcClientConnection conn = cmd->conn;
1235 unsigned char port[4];
1239 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1244 if (cmd->argc < 2) {
1245 cmd->client->ops->say(cmd->client, conn,
1246 "Usage: /CONNECT <server> [<port>]");
1251 if (cmd->argc == 3) {
1252 tmp = atoi(cmd->argv[2]);
1253 SILC_PUT32_MSB(tmp, port);
1257 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1259 strlen(cmd->argv[1]),
1262 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1264 strlen(cmd->argv[1]));
1265 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1266 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1267 silc_buffer_free(buffer);
1269 /* Notify application */
1273 silc_client_command_free(cmd);
1276 SILC_CLIENT_CMD_FUNC(restart)
1280 /* CLOSE command. Close server connection to the remote server */
1282 SILC_CLIENT_CMD_FUNC(close)
1284 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1285 SilcClientConnection conn = cmd->conn;
1287 unsigned char port[4];
1291 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1296 if (cmd->argc < 2) {
1297 cmd->client->ops->say(cmd->client, conn,
1298 "Usage: /CLOSE <server> [<port>]");
1303 if (cmd->argc == 3) {
1304 tmp = atoi(cmd->argv[2]);
1305 SILC_PUT32_MSB(tmp, port);
1309 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1311 strlen(cmd->argv[1]),
1314 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1316 strlen(cmd->argv[1]));
1317 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1318 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1319 silc_buffer_free(buffer);
1321 /* Notify application */
1325 silc_client_command_free(cmd);
1328 /* SHUTDOWN command. Shutdowns the server. */
1330 SILC_CLIENT_CMD_FUNC(shutdown)
1332 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1335 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1340 /* Send the command */
1341 silc_client_send_command(cmd->client, cmd->conn,
1342 SILC_COMMAND_SHUTDOWN, 0, 0);
1344 /* Notify application */
1348 silc_client_command_free(cmd);
1351 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1353 SILC_CLIENT_CMD_FUNC(leave)
1355 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1356 SilcClientConnection conn = cmd->conn;
1357 SilcIDCacheEntry id_cache = NULL;
1358 SilcChannelEntry channel;
1359 SilcBuffer buffer, idp;
1363 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1368 if (cmd->argc != 2) {
1369 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1374 if (cmd->argv[1][0] == '*') {
1375 if (!conn->current_channel) {
1376 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1380 name = conn->current_channel->channel_name;
1382 name = cmd->argv[1];
1385 if (!conn->current_channel) {
1386 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1391 /* Get the Channel ID of the channel */
1392 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1393 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1398 channel = (SilcChannelEntry)id_cache->context;
1400 /* Send LEAVE command to the server */
1401 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1402 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1403 1, idp->data, idp->len);
1404 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1405 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1406 silc_buffer_free(buffer);
1407 silc_buffer_free(idp);
1409 /* We won't talk anymore on this channel */
1410 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1412 conn->current_channel = NULL;
1414 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1415 silc_free(channel->channel_name);
1416 silc_free(channel->id);
1417 silc_free(channel->key);
1418 silc_cipher_free(channel->channel_key);
1421 /* Notify application */
1425 silc_client_command_free(cmd);
1428 /* Command USERS. Requests the USERS of the clients joined on requested
1431 SILC_CLIENT_CMD_FUNC(users)
1433 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1434 SilcClientConnection conn = cmd->conn;
1435 SilcIDCacheEntry id_cache = NULL;
1436 SilcChannelEntry channel;
1437 SilcBuffer buffer, idp;
1438 char *name, *line = NULL;
1439 unsigned int line_len = 0;
1442 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1447 if (cmd->argc != 2) {
1448 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1453 if (cmd->argv[1][0] == '*') {
1454 if (!conn->current_channel) {
1455 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1459 name = conn->current_channel->channel_name;
1461 name = cmd->argv[1];
1464 if (!conn->current_channel) {
1465 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1470 /* Get the Channel ID of the channel */
1471 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1472 /* XXX should resolve the channel ID; LIST command */
1473 cmd->client->ops->say(cmd->client, conn,
1474 "You are not on that channel", name);
1479 channel = (SilcChannelEntry)id_cache->context;
1481 if (!cmd->pending) {
1482 /* Send USERS command to the server */
1483 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1484 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1485 1, idp->data, idp->len);
1486 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1487 NULL, 0, NULL, NULL, buffer->data,
1489 silc_buffer_free(buffer);
1490 silc_buffer_free(idp);
1492 /* Register pending callback which will recall this command callback with
1493 same context and reprocesses the command. When reprocessing we actually
1494 display the information on the screen. */
1495 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1496 silc_client_command_destructor,
1497 silc_client_command_users,
1498 silc_client_command_dup(cmd));
1499 cmd->pending = TRUE;
1504 /* Pending command. Now we've resolved the information from server and
1505 we are ready to display the information on screen. */
1507 SilcChannelUser chu;
1509 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1510 channel->channel_name);
1512 line = silc_calloc(4096, sizeof(*line));
1514 silc_list_start(channel->clients);
1515 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1516 SilcClientEntry e = chu->client;
1517 char *m, tmp[80], len1;
1519 memset(line, 0, sizeof(line_len));
1521 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1523 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1524 line = silc_calloc(line_len, sizeof(*line));
1527 memset(tmp, 0, sizeof(tmp));
1528 m = silc_client_chumode_char(chu->mode);
1530 strncat(line, " ", 1);
1531 strncat(line, e->nickname, strlen(e->nickname));
1532 strncat(line, e->server ? "@" : "", 1);
1536 len1 = strlen(e->server);
1537 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1539 len1 = strlen(line);
1541 memset(&line[29], 0, len1 - 29);
1543 for (i = 0; i < 30 - len1 - 1; i++)
1547 strncat(line, " H", 3);
1548 strcat(tmp, m ? m : "");
1549 strncat(line, tmp, strlen(tmp));
1551 if (strlen(tmp) < 5)
1552 for (i = 0; i < 5 - strlen(tmp); i++)
1555 strcat(line, e->username ? e->username : "");
1557 cmd->client->ops->say(cmd->client, conn, "%s", line);
1567 /* Notify application */
1571 silc_client_command_free(cmd);