5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "clientlibincludes.h"
24 /* Client command list. */
25 SilcClientCommand silc_command_list[] =
27 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
28 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
29 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
30 SILC_CF_LAG | SILC_CF_REG, 3),
31 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
32 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
33 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
34 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
35 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
36 SILC_CLIENT_CMD(kill, KILL, "KILL",
37 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
38 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
39 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
40 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
41 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
42 SILC_CLIENT_CMD(oper, OPER, "OPER",
43 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
44 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 4),
45 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
46 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
48 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
49 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
50 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
51 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
52 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
53 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54 SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
55 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
56 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
57 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
58 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
59 SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
61 { NULL, 0, NULL, 0, 0 },
64 #define SILC_NOT_CONNECTED(x, c) \
65 x->ops->say((x), (c), \
66 "You are not connected to a server, use /SERVER to connect");
68 /* Command operation that is called at the end of all commands.
70 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
71 cmd, TRUE, cmd->command->cmd)
73 /* Error to application. Usage: COMMAND_ERROR; */
74 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
75 cmd, FALSE, cmd->command->cmd)
77 /* Generic function to send any command. The arguments must be sent already
78 encoded into correct form 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 /* silc_client_get_client completion callback */
221 void silc_client_command_completion(SilcClient client,
222 SilcClientConnection conn,
223 SilcClientEntry clients,
224 unsigned int clients_count,
230 /* Command WHOIS. This command is used to query information about
233 SILC_CLIENT_CMD_FUNC(whois)
235 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
236 SilcClientConnection conn = cmd->conn;
240 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
245 if (cmd->argc < 2 || cmd->argc > 3) {
246 cmd->client->ops->say(cmd->client, conn,
247 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
252 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
253 cmd->argc - 1, ++cmd->argv,
254 ++cmd->argv_lens, ++cmd->argv_types,
256 silc_client_packet_send(cmd->client, cmd->conn->sock,
257 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
258 buffer->data, buffer->len, TRUE);
259 silc_buffer_free(buffer);
264 /* Notify application */
268 silc_client_command_free(cmd);
271 SILC_CLIENT_CMD_FUNC(whowas)
275 /* Command IDENTIFY. This command is used to query information about
276 specific user, especially ID's. */
278 SILC_CLIENT_CMD_FUNC(identify)
280 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
281 SilcClientConnection conn = cmd->conn;
285 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
290 if (cmd->argc < 2 || cmd->argc > 3) {
291 cmd->client->ops->say(cmd->client, conn,
292 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
297 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
298 cmd->argc - 1, ++cmd->argv,
299 ++cmd->argv_lens, ++cmd->argv_types,
301 silc_client_packet_send(cmd->client, cmd->conn->sock,
302 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
303 buffer->data, buffer->len, TRUE);
304 silc_buffer_free(buffer);
309 /* Notify application */
313 silc_client_command_free(cmd);
316 /* Command NICK. Shows current nickname/sets new nickname on current
319 SILC_CLIENT_CMD_FUNC(nick)
321 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
322 SilcClientConnection conn = cmd->conn;
326 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
331 if (!strcmp(conn->nickname, cmd->argv[1]))
334 /* Show current nickname */
337 cmd->client->ops->say(cmd->client, conn,
338 "Your nickname is %s on server %s",
339 conn->nickname, conn->remote_host);
341 cmd->client->ops->say(cmd->client, conn,
342 "Your nickname is %s", conn->nickname);
345 /* XXX Notify application */
350 /* Set new nickname */
351 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
352 cmd->argc - 1, ++cmd->argv,
353 ++cmd->argv_lens, ++cmd->argv_types,
355 silc_client_packet_send(cmd->client, cmd->conn->sock,
356 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
357 buffer->data, buffer->len, TRUE);
358 silc_buffer_free(buffer);
363 silc_free(conn->nickname);
364 conn->nickname = strdup(cmd->argv[1]);
366 /* Notify application */
370 silc_client_command_free(cmd);
373 SILC_CLIENT_CMD_FUNC(list)
377 /* Command TOPIC. Sets/shows topic on a channel. */
379 SILC_CLIENT_CMD_FUNC(topic)
381 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
382 SilcClientConnection conn = cmd->conn;
383 SilcIDCacheEntry id_cache = NULL;
384 SilcChannelEntry channel;
385 SilcBuffer buffer, idp;
389 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
394 if (cmd->argc < 2 || cmd->argc > 3) {
395 cmd->client->ops->say(cmd->client, conn,
396 "Usage: /TOPIC <channel> [<topic>]");
401 if (cmd->argv[1][0] == '*') {
402 if (!conn->current_channel) {
403 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
407 name = conn->current_channel->channel_name;
412 if (!conn->current_channel) {
413 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
418 /* Get the Channel ID of the channel */
419 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
420 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
425 channel = (SilcChannelEntry)id_cache->context;
427 /* Send TOPIC command to the server */
428 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
430 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
431 1, idp->data, idp->len,
433 strlen(cmd->argv[2]));
435 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
436 1, idp->data, idp->len,
438 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
439 0, NULL, NULL, buffer->data, buffer->len, TRUE);
440 silc_buffer_free(buffer);
441 silc_buffer_free(idp);
443 /* Notify application */
447 silc_client_command_free(cmd);
450 /* Command INVITE. Invites specific client to join a channel. */
452 SILC_CLIENT_CMD_FUNC(invite)
454 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
455 SilcClient client = cmd->client;
456 SilcClientConnection conn = cmd->conn;
457 SilcClientEntry client_entry;
458 SilcChannelEntry channel_entry;
459 SilcBuffer buffer, clidp, chidp;
460 unsigned int num = 0;
461 char *nickname = NULL, *server = NULL;
464 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
469 if (cmd->argc != 3) {
470 cmd->client->ops->say(cmd->client, conn,
471 "Usage: /INVITE <nickname>[@<server>] <channel>");
476 /* Parse the typed nickname. */
477 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
478 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
483 /* Find client entry */
484 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
492 /* Client entry not found, it was requested thus mark this to be
494 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
495 silc_client_command_destructor,
496 silc_client_command_invite,
497 silc_client_command_dup(cmd));
502 /* Find channel entry */
503 channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
504 if (!channel_entry) {
505 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
511 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
512 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
513 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
514 1, clidp->data, clidp->len,
515 2, chidp->data, chidp->len);
516 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
517 0, NULL, NULL, buffer->data, buffer->len, TRUE);
518 silc_buffer_free(buffer);
519 silc_buffer_free(clidp);
520 silc_buffer_free(chidp);
522 cmd->client->ops->say(cmd->client, conn,
523 "Inviting %s to channel %s", cmd->argv[1],
526 /* Notify application */
530 silc_client_command_free(cmd);
535 SilcClientConnection conn;
538 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
540 QuitInternal q = (QuitInternal)context;
542 /* Close connection */
543 q->client->ops->disconnect(q->client, q->conn);
544 silc_client_close_connection(q->client, q->conn->sock->user_data);
549 /* Command QUIT. Closes connection with current server. */
551 SILC_CLIENT_CMD_FUNC(quit)
553 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
558 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
564 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
565 &cmd->argv[1], &cmd->argv_lens[1],
566 &cmd->argv_types[1], 0);
568 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
569 NULL, NULL, NULL, 0);
570 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
572 buffer->data, buffer->len, TRUE);
573 silc_buffer_free(buffer);
575 q = silc_calloc(1, sizeof(*q));
576 q->client = cmd->client;
579 /* We quit the connection with little timeout */
580 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
581 silc_client_command_quit_cb, (void *)q,
582 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
584 /* Notify application */
588 silc_client_command_free(cmd);
591 SILC_CLIENT_CMD_FUNC(kill)
595 /* Command INFO. Request information about specific server. If specific
596 server is not provided the current server is used. */
598 SILC_CLIENT_CMD_FUNC(info)
600 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
601 SilcClientConnection conn = cmd->conn;
606 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
612 name = strdup(conn->remote_host);
614 name = strdup(cmd->argv[1]);
616 /* Send the command */
617 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
618 1, name, strlen(name));
619 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
620 0, NULL, NULL, buffer->data, buffer->len, TRUE);
621 silc_buffer_free(buffer);
623 /* Notify application */
627 silc_client_command_free(cmd);
630 /* Command PING. Sends ping to server. This is used to test the
631 communication channel. */
633 SILC_CLIENT_CMD_FUNC(ping)
635 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
636 SilcClientConnection conn = cmd->conn;
643 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
648 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
649 name = strdup(conn->remote_host);
651 /* Send the command */
652 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
653 1, conn->remote_id_data,
655 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
656 0, NULL, NULL, buffer->data, buffer->len, TRUE);
657 silc_buffer_free(buffer);
659 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
662 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
667 /* Start counting time */
668 for (i = 0; i < conn->ping_count; i++) {
669 if (conn->ping[i].dest_id == NULL) {
670 conn->ping[i].start_time = time(NULL);
671 conn->ping[i].dest_id = id;
672 conn->ping[i].dest_name = name;
677 if (i >= conn->ping_count) {
678 i = conn->ping_count;
679 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
680 conn->ping[i].start_time = time(NULL);
681 conn->ping[i].dest_id = id;
682 conn->ping[i].dest_name = name;
686 /* Notify application */
690 silc_client_command_free(cmd);
693 SILC_CLIENT_CMD_FUNC(notice)
697 /* Command JOIN. Joins to a channel. */
699 SILC_CLIENT_CMD_FUNC(join)
701 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
702 SilcClientConnection conn = cmd->conn;
703 SilcIDCacheEntry id_cache = NULL;
704 SilcBuffer buffer, idp;
707 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
713 /* Show channels currently joined to */
718 /* See if we have joined to the requested channel already */
719 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
721 cmd->client->ops->say(cmd->client, conn,
722 "You are talking to channel %s", cmd->argv[1]);
723 conn->current_channel = (SilcChannelEntry)id_cache->context;
725 cmd->client->screen->bottom_line->channel = cmd->argv[1];
726 silc_screen_print_bottom_line(cmd->client->screen, 0);
731 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
733 /* Send JOIN command to the server */
736 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
737 1, cmd->argv[1], cmd->argv_lens[1],
738 2, idp->data, idp->len);
739 else if (cmd->argc == 3)
742 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
743 1, cmd->argv[1], cmd->argv_lens[1],
744 2, idp->data, idp->len,
745 3, cmd->argv[2], cmd->argv_lens[2]);
748 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
749 1, cmd->argv[1], cmd->argv_lens[1],
750 2, idp->data, idp->len,
751 3, cmd->argv[2], cmd->argv_lens[2],
752 4, cmd->argv[3], cmd->argv_lens[3]);
754 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
755 0, NULL, NULL, buffer->data, buffer->len, TRUE);
756 silc_buffer_free(buffer);
757 silc_buffer_free(idp);
759 /* Notify application */
763 silc_client_command_free(cmd);
766 /* MOTD command. Requests motd from server. */
768 SILC_CLIENT_CMD_FUNC(motd)
770 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
771 SilcClientConnection conn = cmd->conn;
775 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
780 if (cmd->argc < 1 || cmd->argc > 1) {
781 cmd->client->ops->say(cmd->client, conn,
787 /* Send TOPIC command to the server */
788 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
789 2, conn->remote_host,
790 strlen(conn->remote_host));
791 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
792 0, NULL, NULL, buffer->data, buffer->len, TRUE);
793 silc_buffer_free(buffer);
795 /* Notify application */
799 silc_client_command_free(cmd);
802 /* UMODE. Set user mode in SILC. */
804 SILC_CLIENT_CMD_FUNC(umode)
809 /* CMODE command. Sets channel mode. Modes that does not require any arguments
810 can be set several at once. Those modes that require argument must be set
811 separately (unless set with modes that does not require arguments). */
813 SILC_CLIENT_CMD_FUNC(cmode)
815 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
816 SilcClientConnection conn = cmd->conn;
817 SilcChannelEntry channel;
818 SilcBuffer buffer, chidp;
819 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
820 unsigned int mode, add, type, len, arg_len = 0;
824 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
830 cmd->client->ops->say(cmd->client, conn,
831 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
836 if (cmd->argv[1][0] == '*') {
837 if (!conn->current_channel) {
838 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
843 channel = conn->current_channel;
847 channel = silc_client_get_channel(cmd->client, conn, name);
849 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
855 mode = channel->mode;
857 /* Are we adding or removing mode */
858 if (cmd->argv[2][0] == '-')
863 /* Argument type to be sent to server */
867 cp = cmd->argv[2] + 1;
869 for (i = 0; i < len; i++) {
873 mode |= SILC_CHANNEL_MODE_PRIVATE;
875 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
879 mode |= SILC_CHANNEL_MODE_SECRET;
881 mode &= ~SILC_CHANNEL_MODE_SECRET;
885 mode |= SILC_CHANNEL_MODE_PRIVKEY;
887 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
891 mode |= SILC_CHANNEL_MODE_INVITE;
893 mode &= ~SILC_CHANNEL_MODE_INVITE;
897 mode |= SILC_CHANNEL_MODE_TOPIC;
899 mode &= ~SILC_CHANNEL_MODE_TOPIC;
904 mode |= SILC_CHANNEL_MODE_ULIMIT;
906 ll = atoi(cmd->argv[3]);
907 SILC_PUT32_MSB(ll, tmp);
911 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
916 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
919 arg_len = cmd->argv_lens[3];
921 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
926 mode |= SILC_CHANNEL_MODE_BAN;
929 arg_len = cmd->argv_lens[3];
931 mode &= ~SILC_CHANNEL_MODE_BAN;
936 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
939 arg_len = cmd->argv_lens[3];
941 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
946 mode |= SILC_CHANNEL_MODE_CIPHER;
949 arg_len = cmd->argv_lens[3];
951 mode &= ~SILC_CHANNEL_MODE_CIPHER;
961 if (type && cmd->argc < 3) {
966 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
967 SILC_PUT32_MSB(mode, modebuf);
969 /* Send the command packet. We support sending only one mode at once
970 that requires an argument. */
973 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
974 1, chidp->data, chidp->len,
975 2, modebuf, sizeof(modebuf),
979 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
980 1, chidp->data, chidp->len,
981 2, modebuf, sizeof(modebuf));
984 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
985 0, NULL, NULL, buffer->data, buffer->len, TRUE);
986 silc_buffer_free(buffer);
987 silc_buffer_free(chidp);
989 /* Notify application */
993 silc_client_command_free(cmd);
996 /* CUMODE command. Changes client's mode on a channel. */
998 SILC_CLIENT_CMD_FUNC(cumode)
1000 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1001 SilcClientConnection conn = cmd->conn;
1002 SilcChannelEntry channel;
1003 SilcChannelUser chu;
1004 SilcClientEntry client_entry;
1005 SilcBuffer buffer, clidp, chidp;
1006 unsigned char *name, *cp, modebuf[4];
1007 unsigned int mode = 0, add, len;
1008 char *nickname = NULL, *server = NULL;
1009 unsigned int num = 0;
1013 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1018 if (cmd->argc < 4) {
1019 cmd->client->ops->say(cmd->client, conn,
1020 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1025 if (cmd->argv[1][0] == '*') {
1026 if (!conn->current_channel) {
1027 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1032 channel = conn->current_channel;
1034 name = cmd->argv[1];
1036 channel = silc_client_get_channel(cmd->client, conn, name);
1038 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1044 /* Parse the typed nickname. */
1045 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1046 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1051 /* Find client entry */
1052 client_entry = silc_idlist_get_client(cmd->client, conn,
1053 nickname, server, num, TRUE);
1054 if (!client_entry) {
1055 /* Client entry not found, it was requested thus mark this to be
1057 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1058 silc_client_command_destructor,
1059 silc_client_command_cumode,
1060 silc_client_command_dup(cmd));
1065 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1066 if (chu->client == client_entry) {
1072 /* Are we adding or removing mode */
1073 if (cmd->argv[2][0] == '-')
1079 cp = cmd->argv[2] + 1;
1081 for (i = 0; i < len; i++) {
1085 mode |= SILC_CHANNEL_UMODE_CHANFO;
1086 mode |= SILC_CHANNEL_UMODE_CHANOP;
1088 mode = SILC_CHANNEL_UMODE_NONE;
1093 mode |= SILC_CHANNEL_UMODE_CHANFO;
1095 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1099 mode |= SILC_CHANNEL_UMODE_CHANOP;
1101 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1110 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1111 SILC_PUT32_MSB(mode, modebuf);
1112 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1114 /* Send the command packet. We support sending only one mode at once
1115 that requires an argument. */
1116 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1117 1, chidp->data, chidp->len,
1119 3, clidp->data, clidp->len);
1121 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1122 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1123 silc_buffer_free(buffer);
1124 silc_buffer_free(chidp);
1125 silc_buffer_free(clidp);
1127 /* Notify application */
1131 silc_client_command_free(cmd);
1134 /* KICK command. Kicks a client out of channel. */
1136 SILC_CLIENT_CMD_FUNC(kick)
1138 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1139 SilcClientConnection conn = cmd->conn;
1140 SilcIDCacheEntry id_cache = NULL;
1141 SilcChannelEntry channel;
1142 SilcBuffer buffer, idp, idp2;
1143 SilcClientEntry target;
1145 unsigned int num = 0;
1146 char *nickname = NULL, *server = NULL;
1149 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1154 if (cmd->argc < 3) {
1155 cmd->client->ops->say(cmd->client, conn,
1156 "Usage: /KICK <channel> <client> [<comment>]");
1161 if (cmd->argv[1][0] == '*') {
1162 if (!conn->current_channel) {
1163 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1167 name = conn->current_channel->channel_name;
1169 name = cmd->argv[1];
1172 if (!conn->current_channel) {
1173 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1178 /* Get the Channel ID of the channel */
1179 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1180 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1185 channel = (SilcChannelEntry)id_cache->context;
1187 /* Parse the typed nickname. */
1188 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1189 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1194 /* Get the target client */
1195 target = silc_idlist_get_client(cmd->client, conn, nickname,
1196 server, num, FALSE);
1198 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1204 /* Send KICK command to the server */
1205 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1206 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1208 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1209 1, idp->data, idp->len,
1210 2, idp2->data, idp2->len);
1212 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1213 1, idp->data, idp->len,
1214 2, idp2->data, idp2->len,
1216 strlen(cmd->argv[3]));
1217 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1218 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1219 silc_buffer_free(buffer);
1220 silc_buffer_free(idp);
1221 silc_buffer_free(idp2);
1223 /* Notify application */
1227 silc_client_command_free(cmd);
1230 SILC_CLIENT_CMD_FUNC(silcoper)
1234 SILC_CLIENT_CMD_FUNC(oper)
1238 /* CONNECT command. Connects the server to another server. */
1240 SILC_CLIENT_CMD_FUNC(connect)
1242 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1243 SilcClientConnection conn = cmd->conn;
1245 unsigned char port[4];
1249 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1254 if (cmd->argc < 2) {
1255 cmd->client->ops->say(cmd->client, conn,
1256 "Usage: /CONNECT <server> [<port>]");
1261 if (cmd->argc == 3) {
1262 tmp = atoi(cmd->argv[2]);
1263 SILC_PUT32_MSB(tmp, port);
1267 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1269 strlen(cmd->argv[1]),
1272 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1274 strlen(cmd->argv[1]));
1275 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1276 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1277 silc_buffer_free(buffer);
1279 /* Notify application */
1283 silc_client_command_free(cmd);
1286 SILC_CLIENT_CMD_FUNC(restart)
1290 /* CLOSE command. Close server connection to the remote server */
1292 SILC_CLIENT_CMD_FUNC(close)
1294 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1295 SilcClientConnection conn = cmd->conn;
1297 unsigned char port[4];
1301 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1306 if (cmd->argc < 2) {
1307 cmd->client->ops->say(cmd->client, conn,
1308 "Usage: /CLOSE <server> [<port>]");
1313 if (cmd->argc == 3) {
1314 tmp = atoi(cmd->argv[2]);
1315 SILC_PUT32_MSB(tmp, port);
1319 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1321 strlen(cmd->argv[1]),
1324 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1326 strlen(cmd->argv[1]));
1327 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1328 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1329 silc_buffer_free(buffer);
1331 /* Notify application */
1335 silc_client_command_free(cmd);
1338 /* SHUTDOWN command. Shutdowns the server. */
1340 SILC_CLIENT_CMD_FUNC(shutdown)
1342 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1345 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1350 /* Send the command */
1351 silc_client_send_command(cmd->client, cmd->conn,
1352 SILC_COMMAND_SHUTDOWN, 0, 0);
1354 /* Notify application */
1358 silc_client_command_free(cmd);
1361 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1363 SILC_CLIENT_CMD_FUNC(leave)
1365 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1366 SilcClientConnection conn = cmd->conn;
1367 SilcIDCacheEntry id_cache = NULL;
1368 SilcChannelEntry channel;
1369 SilcBuffer buffer, idp;
1373 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1378 if (cmd->argc != 2) {
1379 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1384 if (cmd->argv[1][0] == '*') {
1385 if (!conn->current_channel) {
1386 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1390 name = conn->current_channel->channel_name;
1392 name = cmd->argv[1];
1395 if (!conn->current_channel) {
1396 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1401 /* Get the Channel ID of the channel */
1402 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1403 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1408 channel = (SilcChannelEntry)id_cache->context;
1410 /* Send LEAVE command to the server */
1411 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1412 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1413 1, idp->data, idp->len);
1414 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1415 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1416 silc_buffer_free(buffer);
1417 silc_buffer_free(idp);
1419 /* We won't talk anymore on this channel */
1420 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1422 conn->current_channel = NULL;
1424 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1425 silc_free(channel->channel_name);
1426 silc_free(channel->id);
1427 silc_free(channel->key);
1428 silc_cipher_free(channel->channel_key);
1431 /* Notify application */
1435 silc_client_command_free(cmd);
1438 /* Command USERS. Requests the USERS of the clients joined on requested
1441 SILC_CLIENT_CMD_FUNC(users)
1443 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1444 SilcClientConnection conn = cmd->conn;
1445 SilcIDCacheEntry id_cache = NULL;
1446 SilcChannelEntry channel;
1447 SilcBuffer buffer, idp;
1448 char *name, *line = NULL;
1449 unsigned int line_len = 0;
1452 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1457 if (cmd->argc != 2) {
1458 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1463 if (cmd->argv[1][0] == '*') {
1464 if (!conn->current_channel) {
1465 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1469 name = conn->current_channel->channel_name;
1471 name = cmd->argv[1];
1474 if (!conn->current_channel) {
1475 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1480 /* Get the Channel ID of the channel */
1481 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1482 /* XXX should resolve the channel ID; LIST command */
1483 cmd->client->ops->say(cmd->client, conn,
1484 "You are not on that channel", name);
1489 channel = (SilcChannelEntry)id_cache->context;
1491 if (!cmd->pending) {
1492 /* Send USERS command to the server */
1493 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1494 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1495 1, idp->data, idp->len);
1496 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1497 NULL, 0, NULL, NULL, buffer->data,
1499 silc_buffer_free(buffer);
1500 silc_buffer_free(idp);
1502 /* Register pending callback which will recall this command callback with
1503 same context and reprocesses the command. When reprocessing we actually
1504 display the information on the screen. */
1505 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1506 silc_client_command_destructor,
1507 silc_client_command_users,
1508 silc_client_command_dup(cmd));
1509 cmd->pending = TRUE;
1514 /* Pending command. Now we've resolved the information from server and
1515 we are ready to display the information on screen. */
1517 SilcChannelUser chu;
1519 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1520 channel->channel_name);
1522 line = silc_calloc(4096, sizeof(*line));
1524 silc_list_start(channel->clients);
1525 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1526 SilcClientEntry e = chu->client;
1527 char *m, tmp[80], len1;
1529 memset(line, 0, sizeof(line_len));
1531 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1533 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1534 line = silc_calloc(line_len, sizeof(*line));
1537 memset(tmp, 0, sizeof(tmp));
1538 m = silc_client_chumode_char(chu->mode);
1540 strncat(line, " ", 1);
1541 strncat(line, e->nickname, strlen(e->nickname));
1542 strncat(line, e->server ? "@" : "", 1);
1546 len1 = strlen(e->server);
1547 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1549 len1 = strlen(line);
1551 memset(&line[29], 0, len1 - 29);
1553 for (i = 0; i < 30 - len1 - 1; i++)
1557 strncat(line, " H", 3);
1558 strcat(tmp, m ? m : "");
1559 strncat(line, tmp, strlen(tmp));
1561 if (strlen(tmp) < 5)
1562 for (i = 0; i < 5 - strlen(tmp); i++)
1565 strcat(line, e->username ? e->username : "");
1567 cmd->client->ops->say(cmd->client, conn, "%s", line);
1577 /* Notify application */
1581 silc_client_command_free(cmd);