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];
1238 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1243 if (cmd->argc < 2) {
1244 cmd->client->ops->say(cmd->client, conn,
1245 "Usage: /CONNECT <server> [<port>]");
1251 SILC_PUT32_MSB(atoi(cmd->argv[2]), port);
1254 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1256 strlen(cmd->argv[1]),
1259 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1261 strlen(cmd->argv[1]));
1262 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1263 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1264 silc_buffer_free(buffer);
1266 /* Notify application */
1270 silc_client_command_free(cmd);
1273 SILC_CLIENT_CMD_FUNC(restart)
1277 /* CLOSE command. Close server connection to the remote server */
1279 SILC_CLIENT_CMD_FUNC(close)
1281 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1282 SilcClientConnection conn = cmd->conn;
1284 unsigned char port[4];
1287 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1292 if (cmd->argc < 2) {
1293 cmd->client->ops->say(cmd->client, conn,
1294 "Usage: /CLOSE <server> [<port>]");
1300 SILC_PUT32_MSB(atoi(cmd->argv[2]), port);
1303 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1305 strlen(cmd->argv[1]),
1308 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1310 strlen(cmd->argv[1]));
1311 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1312 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1313 silc_buffer_free(buffer);
1315 /* Notify application */
1319 silc_client_command_free(cmd);
1322 /* SHUTDOWN command. Shutdowns the server. */
1324 SILC_CLIENT_CMD_FUNC(shutdown)
1326 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1329 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1334 /* Send the command */
1335 silc_client_send_command(cmd->client, cmd->conn,
1336 SILC_COMMAND_SHUTDOWN, 0, 0);
1338 /* Notify application */
1342 silc_client_command_free(cmd);
1345 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1347 SILC_CLIENT_CMD_FUNC(leave)
1349 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1350 SilcClientConnection conn = cmd->conn;
1351 SilcIDCacheEntry id_cache = NULL;
1352 SilcChannelEntry channel;
1353 SilcBuffer buffer, idp;
1357 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1362 if (cmd->argc != 2) {
1363 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1368 if (cmd->argv[1][0] == '*') {
1369 if (!conn->current_channel) {
1370 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1374 name = conn->current_channel->channel_name;
1376 name = cmd->argv[1];
1379 if (!conn->current_channel) {
1380 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1385 /* Get the Channel ID of the channel */
1386 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1387 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1392 channel = (SilcChannelEntry)id_cache->context;
1394 /* Send LEAVE command to the server */
1395 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1396 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1397 1, idp->data, idp->len);
1398 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1399 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1400 silc_buffer_free(buffer);
1401 silc_buffer_free(idp);
1403 /* We won't talk anymore on this channel */
1404 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1406 conn->current_channel = NULL;
1408 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1409 silc_free(channel->channel_name);
1410 silc_free(channel->id);
1411 silc_free(channel->key);
1412 silc_cipher_free(channel->channel_key);
1415 /* Notify application */
1419 silc_client_command_free(cmd);
1422 /* Command USERS. Requests the USERS of the clients joined on requested
1425 SILC_CLIENT_CMD_FUNC(users)
1427 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1428 SilcClientConnection conn = cmd->conn;
1429 SilcIDCacheEntry id_cache = NULL;
1430 SilcChannelEntry channel;
1431 SilcBuffer buffer, idp;
1432 char *name, *line = NULL;
1433 unsigned int line_len = 0;
1436 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1441 if (cmd->argc != 2) {
1442 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1447 if (cmd->argv[1][0] == '*') {
1448 if (!conn->current_channel) {
1449 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1453 name = conn->current_channel->channel_name;
1455 name = cmd->argv[1];
1458 if (!conn->current_channel) {
1459 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1464 /* Get the Channel ID of the channel */
1465 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1466 /* XXX should resolve the channel ID; LIST command */
1467 cmd->client->ops->say(cmd->client, conn,
1468 "You are not on that channel", name);
1473 channel = (SilcChannelEntry)id_cache->context;
1475 if (!cmd->pending) {
1476 /* Send USERS command to the server */
1477 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1478 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1479 1, idp->data, idp->len);
1480 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1481 NULL, 0, NULL, NULL, buffer->data,
1483 silc_buffer_free(buffer);
1484 silc_buffer_free(idp);
1486 /* Register pending callback which will recall this command callback with
1487 same context and reprocesses the command. When reprocessing we actually
1488 display the information on the screen. */
1489 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1490 silc_client_command_destructor,
1491 silc_client_command_users,
1492 silc_client_command_dup(cmd));
1493 cmd->pending = TRUE;
1498 /* Pending command. Now we've resolved the information from server and
1499 we are ready to display the information on screen. */
1501 SilcChannelUser chu;
1503 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1504 channel->channel_name);
1506 line = silc_calloc(4096, sizeof(*line));
1508 silc_list_start(channel->clients);
1509 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1510 SilcClientEntry e = chu->client;
1511 char *m, tmp[80], len1;
1513 memset(line, 0, sizeof(line_len));
1515 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1517 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1518 line = silc_calloc(line_len, sizeof(*line));
1521 memset(tmp, 0, sizeof(tmp));
1522 m = silc_client_chumode_char(chu->mode);
1524 strncat(line, " ", 1);
1525 strncat(line, e->nickname, strlen(e->nickname));
1526 strncat(line, e->server ? "@" : "", 1);
1530 len1 = strlen(e->server);
1531 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1533 len1 = strlen(line);
1535 memset(&line[29], 0, len1 - 29);
1537 for (i = 0; i < 30 - len1 - 1; i++)
1541 strncat(line, " H", 3);
1542 strcat(tmp, m ? m : "");
1543 strncat(line, tmp, strlen(tmp));
1545 if (strlen(tmp) < 5)
1546 for (i = 0; i < 5 - strlen(tmp); i++)
1549 strcat(line, e->username ? e->username : "");
1551 cmd->client->ops->say(cmd->client, conn, "%s", line);
1561 /* Notify application */
1565 silc_client_command_free(cmd);