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"
23 #include "client_internal.h"
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
28 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
31 SILC_CF_LAG | SILC_CF_REG, 3),
32 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37 SILC_CLIENT_CMD(kill, KILL, "KILL",
38 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43 SILC_CLIENT_CMD(oper, OPER, "OPER",
44 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
52 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
54 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
55 SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
56 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
57 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
58 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
59 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
60 SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
62 { NULL, 0, NULL, 0, 0 },
65 #define SILC_NOT_CONNECTED(x, c) \
66 x->ops->say((x), (c), \
67 "You are not connected to a server, use /SERVER to connect");
69 /* Command operation that is called at the end of all commands.
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72 cmd, TRUE, cmd->command->cmd)
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76 cmd, FALSE, cmd->command->cmd)
78 /* Generic function to send any command. The arguments must be sent already
79 encoded into correct form and in correct order. */
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82 SilcCommand command, unsigned short ident,
83 unsigned int argc, ...)
90 packet = silc_command_payload_encode_vap(command, ident, argc, ap);
91 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
92 NULL, 0, NULL, NULL, packet->data,
94 silc_buffer_free(packet);
97 /* Finds and returns a pointer to the command list. Return NULL if the
98 command is not found. */
100 SilcClientCommand *silc_client_command_find(const char *name)
102 SilcClientCommand *cmd;
104 for (cmd = silc_command_list; cmd->name; cmd++) {
105 if (!strcmp(cmd->name, name))
112 /* Add new pending command to be executed when reply to a command has been
113 received. The `reply_cmd' is the command that will call the `callback'
114 with `context' when reply has been received. If `ident is non-zero
115 the `callback' will be executed when received reply with command
116 identifier `ident'. */
118 void silc_client_command_pending(SilcClientConnection conn,
119 SilcCommand reply_cmd,
120 unsigned short ident,
121 SilcClientPendingDestructor destructor,
122 SilcCommandCb callback,
125 SilcClientCommandPending *reply;
127 reply = silc_calloc(1, sizeof(*reply));
128 reply->reply_cmd = reply_cmd;
129 reply->ident = ident;
130 reply->context = context;
131 reply->callback = callback;
132 reply->destructor = destructor;
133 silc_dlist_add(conn->pending_commands, reply);
136 /* Deletes pending command by reply command type. */
138 void silc_client_command_pending_del(SilcClientConnection conn,
139 SilcCommand reply_cmd,
140 unsigned short ident)
142 SilcClientCommandPending *r;
144 silc_dlist_start(conn->pending_commands);
145 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
146 if (r->reply_cmd == reply_cmd && r->ident == ident) {
147 silc_dlist_del(conn->pending_commands, r);
153 /* Checks for pending commands and marks callbacks to be called from
154 the command reply function. Returns TRUE if there were pending command. */
156 int silc_client_command_pending_check(SilcClientConnection conn,
157 SilcClientCommandReplyContext ctx,
159 unsigned short ident)
161 SilcClientCommandPending *r;
163 silc_dlist_start(conn->pending_commands);
164 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
165 if (r->reply_cmd == command && r->ident == ident) {
166 ctx->context = r->context;
167 ctx->callback = r->callback;
168 ctx->destructor = r->destructor;
177 /* Allocate Command Context */
179 SilcClientCommandContext silc_client_command_alloc()
181 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
186 /* Free command context and its internals */
188 void silc_client_command_free(SilcClientCommandContext ctx)
191 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
193 if (ctx->users < 1) {
196 for (i = 0; i < ctx->argc; i++)
197 silc_free(ctx->argv[i]);
202 /* Duplicate Command Context by adding reference counter. The context won't
203 be free'd untill it hits zero. */
205 SilcClientCommandContext 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 /* Command WHOWAS. This command is used to query history information about
272 specific user that used to exist in the network. */
274 SILC_CLIENT_CMD_FUNC(whowas)
276 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
277 SilcClientConnection conn = cmd->conn;
281 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
286 if (cmd->argc < 2 || cmd->argc > 3) {
287 cmd->client->ops->say(cmd->client, conn,
288 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
293 buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
294 cmd->argc - 1, ++cmd->argv,
295 ++cmd->argv_lens, ++cmd->argv_types,
297 silc_client_packet_send(cmd->client, cmd->conn->sock,
298 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
299 buffer->data, buffer->len, TRUE);
300 silc_buffer_free(buffer);
305 /* Notify application */
309 silc_client_command_free(cmd);
312 /* Command IDENTIFY. This command is used to query information about
313 specific user, especially ID's. */
315 SILC_CLIENT_CMD_FUNC(identify)
317 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
318 SilcClientConnection conn = cmd->conn;
322 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
327 if (cmd->argc < 2 || cmd->argc > 3) {
328 cmd->client->ops->say(cmd->client, conn,
329 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
334 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
335 cmd->argc - 1, ++cmd->argv,
336 ++cmd->argv_lens, ++cmd->argv_types,
338 silc_client_packet_send(cmd->client, cmd->conn->sock,
339 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
340 buffer->data, buffer->len, TRUE);
341 silc_buffer_free(buffer);
346 /* Notify application */
350 silc_client_command_free(cmd);
353 /* Command NICK. Shows current nickname/sets new nickname on current
356 SILC_CLIENT_CMD_FUNC(nick)
358 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
359 SilcClientConnection conn = cmd->conn;
363 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
368 if (!strcmp(conn->nickname, cmd->argv[1]))
371 /* Show current nickname */
374 cmd->client->ops->say(cmd->client, conn,
375 "Your nickname is %s on server %s",
376 conn->nickname, conn->remote_host);
378 cmd->client->ops->say(cmd->client, conn,
379 "Your nickname is %s", conn->nickname);
382 /* XXX Notify application */
387 /* Set new nickname */
388 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
389 cmd->argc - 1, ++cmd->argv,
390 ++cmd->argv_lens, ++cmd->argv_types,
391 ++cmd->conn->cmd_ident);
392 silc_client_packet_send(cmd->client, cmd->conn->sock,
393 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
394 buffer->data, buffer->len, TRUE);
395 silc_buffer_free(buffer);
400 silc_free(conn->nickname);
401 conn->nickname = strdup(cmd->argv[1]);
403 /* Notify application */
407 silc_client_command_free(cmd);
410 SILC_CLIENT_CMD_FUNC(list)
414 /* Command TOPIC. Sets/shows topic on a channel. */
416 SILC_CLIENT_CMD_FUNC(topic)
418 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
419 SilcClientConnection conn = cmd->conn;
420 SilcIDCacheEntry id_cache = NULL;
421 SilcChannelEntry channel;
422 SilcBuffer buffer, idp;
426 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
431 if (cmd->argc < 2 || cmd->argc > 3) {
432 cmd->client->ops->say(cmd->client, conn,
433 "Usage: /TOPIC <channel> [<topic>]");
438 if (cmd->argv[1][0] == '*') {
439 if (!conn->current_channel) {
440 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
444 name = conn->current_channel->channel_name;
449 if (!conn->current_channel) {
450 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
455 /* Get the Channel ID of the channel */
456 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
457 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
462 channel = (SilcChannelEntry)id_cache->context;
464 /* Send TOPIC command to the server */
465 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
467 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
468 1, idp->data, idp->len,
470 strlen(cmd->argv[2]));
472 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
473 1, idp->data, idp->len,
475 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
476 0, NULL, NULL, buffer->data, buffer->len, TRUE);
477 silc_buffer_free(buffer);
478 silc_buffer_free(idp);
480 /* Notify application */
484 silc_client_command_free(cmd);
487 /* Command INVITE. Invites specific client to join a channel. */
489 SILC_CLIENT_CMD_FUNC(invite)
491 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
492 SilcClient client = cmd->client;
493 SilcClientConnection conn = cmd->conn;
494 SilcClientEntry client_entry;
495 SilcChannelEntry channel_entry;
496 SilcBuffer buffer, clidp, chidp;
497 unsigned int num = 0;
498 char *nickname = NULL, *server = NULL;
501 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
506 if (cmd->argc != 3) {
507 cmd->client->ops->say(cmd->client, conn,
508 "Usage: /INVITE <nickname>[@<server>] <channel>");
513 /* Parse the typed nickname. */
514 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
515 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
520 /* Find client entry */
521 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
529 /* Client entry not found, it was requested thus mark this to be
531 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 0,
532 silc_client_command_destructor,
533 silc_client_command_invite,
534 silc_client_command_dup(cmd));
539 /* Find channel entry */
540 channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
541 if (!channel_entry) {
542 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
548 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
549 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
550 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
551 1, clidp->data, clidp->len,
552 2, chidp->data, chidp->len);
553 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
554 0, NULL, NULL, buffer->data, buffer->len, TRUE);
555 silc_buffer_free(buffer);
556 silc_buffer_free(clidp);
557 silc_buffer_free(chidp);
559 cmd->client->ops->say(cmd->client, conn,
560 "Inviting %s to channel %s", cmd->argv[1],
563 /* Notify application */
567 silc_client_command_free(cmd);
572 SilcClientConnection conn;
575 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
577 QuitInternal q = (QuitInternal)context;
579 /* Close connection */
580 q->client->ops->disconnect(q->client, q->conn);
581 silc_client_close_connection(q->client, q->conn->sock->user_data);
586 /* Command QUIT. Closes connection with current server. */
588 SILC_CLIENT_CMD_FUNC(quit)
590 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
595 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
601 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
602 &cmd->argv[1], &cmd->argv_lens[1],
603 &cmd->argv_types[1], 0);
605 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
606 NULL, NULL, NULL, 0);
607 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
609 buffer->data, buffer->len, TRUE);
610 silc_buffer_free(buffer);
612 q = silc_calloc(1, sizeof(*q));
613 q->client = cmd->client;
616 /* We quit the connection with little timeout */
617 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
618 silc_client_command_quit_cb, (void *)q,
619 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
621 /* Notify application */
625 silc_client_command_free(cmd);
628 SILC_CLIENT_CMD_FUNC(kill)
632 /* Command INFO. Request information about specific server. If specific
633 server is not provided the current server is used. */
635 SILC_CLIENT_CMD_FUNC(info)
637 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
638 SilcClientConnection conn = cmd->conn;
643 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
649 name = strdup(conn->remote_host);
651 name = strdup(cmd->argv[1]);
653 /* Send the command */
654 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
655 1, name, strlen(name));
656 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
657 0, NULL, NULL, buffer->data, buffer->len, TRUE);
658 silc_buffer_free(buffer);
660 /* Notify application */
664 silc_client_command_free(cmd);
667 /* Command PING. Sends ping to server. This is used to test the
668 communication channel. */
670 SILC_CLIENT_CMD_FUNC(ping)
672 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
673 SilcClientConnection conn = cmd->conn;
680 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
685 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
686 name = strdup(conn->remote_host);
688 /* Send the command */
689 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
690 1, conn->remote_id_data,
692 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
693 0, NULL, NULL, buffer->data, buffer->len, TRUE);
694 silc_buffer_free(buffer);
696 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
699 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
704 /* Start counting time */
705 for (i = 0; i < conn->ping_count; i++) {
706 if (conn->ping[i].dest_id == NULL) {
707 conn->ping[i].start_time = time(NULL);
708 conn->ping[i].dest_id = id;
709 conn->ping[i].dest_name = name;
714 if (i >= conn->ping_count) {
715 i = conn->ping_count;
716 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
717 conn->ping[i].start_time = time(NULL);
718 conn->ping[i].dest_id = id;
719 conn->ping[i].dest_name = name;
723 /* Notify application */
727 silc_client_command_free(cmd);
730 SILC_CLIENT_CMD_FUNC(notice)
734 /* Command JOIN. Joins to a channel. */
736 SILC_CLIENT_CMD_FUNC(join)
738 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
739 SilcClientConnection conn = cmd->conn;
740 SilcIDCacheEntry id_cache = NULL;
741 SilcBuffer buffer, idp;
744 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
750 /* Show channels currently joined to */
755 /* See if we have joined to the requested channel already */
756 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
758 cmd->client->ops->say(cmd->client, conn,
759 "You are talking to channel %s", cmd->argv[1]);
760 conn->current_channel = (SilcChannelEntry)id_cache->context;
762 cmd->client->screen->bottom_line->channel = cmd->argv[1];
763 silc_screen_print_bottom_line(cmd->client->screen, 0);
768 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
770 /* Send JOIN command to the server */
773 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
774 1, cmd->argv[1], cmd->argv_lens[1],
775 2, idp->data, idp->len);
776 else if (cmd->argc == 3)
779 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
780 1, cmd->argv[1], cmd->argv_lens[1],
781 2, idp->data, idp->len,
782 3, cmd->argv[2], cmd->argv_lens[2]);
785 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
786 1, cmd->argv[1], cmd->argv_lens[1],
787 2, idp->data, idp->len,
788 3, cmd->argv[2], cmd->argv_lens[2],
789 4, cmd->argv[3], cmd->argv_lens[3]);
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);
794 silc_buffer_free(idp);
796 /* Notify application */
800 silc_client_command_free(cmd);
803 /* MOTD command. Requests motd from server. */
805 SILC_CLIENT_CMD_FUNC(motd)
807 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
808 SilcClientConnection conn = cmd->conn;
812 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
817 if (cmd->argc < 1 || cmd->argc > 1) {
818 cmd->client->ops->say(cmd->client, conn,
824 /* Send TOPIC command to the server */
825 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
826 2, conn->remote_host,
827 strlen(conn->remote_host));
828 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
829 0, NULL, NULL, buffer->data, buffer->len, TRUE);
830 silc_buffer_free(buffer);
832 /* Notify application */
836 silc_client_command_free(cmd);
839 /* UMODE. Set user mode in SILC. */
841 SILC_CLIENT_CMD_FUNC(umode)
846 /* CMODE command. Sets channel mode. Modes that does not require any arguments
847 can be set several at once. Those modes that require argument must be set
848 separately (unless set with modes that does not require arguments). */
850 SILC_CLIENT_CMD_FUNC(cmode)
852 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
853 SilcClientConnection conn = cmd->conn;
854 SilcChannelEntry channel;
855 SilcBuffer buffer, chidp;
856 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
857 unsigned int mode, add, type, len, arg_len = 0;
861 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
867 cmd->client->ops->say(cmd->client, conn,
868 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
873 if (cmd->argv[1][0] == '*') {
874 if (!conn->current_channel) {
875 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
880 channel = conn->current_channel;
884 channel = silc_client_get_channel(cmd->client, conn, name);
886 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
892 mode = channel->mode;
894 /* Are we adding or removing mode */
895 if (cmd->argv[2][0] == '-')
900 /* Argument type to be sent to server */
904 cp = cmd->argv[2] + 1;
906 for (i = 0; i < len; i++) {
910 mode |= SILC_CHANNEL_MODE_PRIVATE;
912 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
916 mode |= SILC_CHANNEL_MODE_SECRET;
918 mode &= ~SILC_CHANNEL_MODE_SECRET;
922 mode |= SILC_CHANNEL_MODE_PRIVKEY;
924 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
928 mode |= SILC_CHANNEL_MODE_INVITE;
930 mode &= ~SILC_CHANNEL_MODE_INVITE;
934 mode |= SILC_CHANNEL_MODE_TOPIC;
936 mode &= ~SILC_CHANNEL_MODE_TOPIC;
941 mode |= SILC_CHANNEL_MODE_ULIMIT;
943 ll = atoi(cmd->argv[3]);
944 SILC_PUT32_MSB(ll, tmp);
948 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
953 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
956 arg_len = cmd->argv_lens[3];
958 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
963 mode |= SILC_CHANNEL_MODE_BAN;
966 arg_len = cmd->argv_lens[3];
968 mode &= ~SILC_CHANNEL_MODE_BAN;
973 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
976 arg_len = cmd->argv_lens[3];
978 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
983 mode |= SILC_CHANNEL_MODE_CIPHER;
986 arg_len = cmd->argv_lens[3];
988 mode &= ~SILC_CHANNEL_MODE_CIPHER;
998 if (type && cmd->argc < 3) {
1003 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1004 SILC_PUT32_MSB(mode, modebuf);
1006 /* Send the command packet. We support sending only one mode at once
1007 that requires an argument. */
1010 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1011 1, chidp->data, chidp->len,
1012 2, modebuf, sizeof(modebuf),
1013 type, arg, arg_len);
1016 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1017 1, chidp->data, chidp->len,
1018 2, modebuf, sizeof(modebuf));
1021 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1022 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1023 silc_buffer_free(buffer);
1024 silc_buffer_free(chidp);
1026 /* Notify application */
1030 silc_client_command_free(cmd);
1033 /* CUMODE command. Changes client's mode on a channel. */
1035 SILC_CLIENT_CMD_FUNC(cumode)
1037 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1038 SilcClientConnection conn = cmd->conn;
1039 SilcChannelEntry channel;
1040 SilcChannelUser chu;
1041 SilcClientEntry client_entry;
1042 SilcBuffer buffer, clidp, chidp;
1043 unsigned char *name, *cp, modebuf[4];
1044 unsigned int mode = 0, add, len;
1045 char *nickname = NULL, *server = NULL;
1046 unsigned int num = 0;
1050 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1055 if (cmd->argc < 4) {
1056 cmd->client->ops->say(cmd->client, conn,
1057 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1062 if (cmd->argv[1][0] == '*') {
1063 if (!conn->current_channel) {
1064 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1069 channel = conn->current_channel;
1071 name = cmd->argv[1];
1073 channel = silc_client_get_channel(cmd->client, conn, name);
1075 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1081 /* Parse the typed nickname. */
1082 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1083 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1088 /* Find client entry */
1089 client_entry = silc_idlist_get_client(cmd->client, conn,
1090 nickname, server, num, TRUE);
1091 if (!client_entry) {
1092 /* Client entry not found, it was requested thus mark this to be
1094 silc_client_command_pending(conn, SILC_COMMAND_CUMODE, 0,
1095 silc_client_command_destructor,
1096 silc_client_command_cumode,
1097 silc_client_command_dup(cmd));
1102 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1103 if (chu->client == client_entry) {
1109 /* Are we adding or removing mode */
1110 if (cmd->argv[2][0] == '-')
1116 cp = cmd->argv[2] + 1;
1118 for (i = 0; i < len; i++) {
1122 mode |= SILC_CHANNEL_UMODE_CHANFO;
1123 mode |= SILC_CHANNEL_UMODE_CHANOP;
1125 mode = SILC_CHANNEL_UMODE_NONE;
1130 mode |= SILC_CHANNEL_UMODE_CHANFO;
1132 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1136 mode |= SILC_CHANNEL_UMODE_CHANOP;
1138 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1147 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1148 SILC_PUT32_MSB(mode, modebuf);
1149 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1151 /* Send the command packet. We support sending only one mode at once
1152 that requires an argument. */
1153 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1154 1, chidp->data, chidp->len,
1156 3, clidp->data, clidp->len);
1158 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1159 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1160 silc_buffer_free(buffer);
1161 silc_buffer_free(chidp);
1162 silc_buffer_free(clidp);
1164 /* Notify application */
1168 silc_client_command_free(cmd);
1171 /* KICK command. Kicks a client out of channel. */
1173 SILC_CLIENT_CMD_FUNC(kick)
1175 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1176 SilcClientConnection conn = cmd->conn;
1177 SilcIDCacheEntry id_cache = NULL;
1178 SilcChannelEntry channel;
1179 SilcBuffer buffer, idp, idp2;
1180 SilcClientEntry target;
1182 unsigned int num = 0;
1183 char *nickname = NULL, *server = NULL;
1186 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1191 if (cmd->argc < 3) {
1192 cmd->client->ops->say(cmd->client, conn,
1193 "Usage: /KICK <channel> <client> [<comment>]");
1198 if (cmd->argv[1][0] == '*') {
1199 if (!conn->current_channel) {
1200 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1204 name = conn->current_channel->channel_name;
1206 name = cmd->argv[1];
1209 if (!conn->current_channel) {
1210 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1215 /* Get the Channel ID of the channel */
1216 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1217 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1222 channel = (SilcChannelEntry)id_cache->context;
1224 /* Parse the typed nickname. */
1225 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1226 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1231 /* Get the target client */
1232 target = silc_idlist_get_client(cmd->client, conn, nickname,
1233 server, num, FALSE);
1235 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1241 /* Send KICK command to the server */
1242 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1243 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1245 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1246 1, idp->data, idp->len,
1247 2, idp2->data, idp2->len);
1249 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1250 1, idp->data, idp->len,
1251 2, idp2->data, idp2->len,
1253 strlen(cmd->argv[3]));
1254 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1255 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1256 silc_buffer_free(buffer);
1257 silc_buffer_free(idp);
1258 silc_buffer_free(idp2);
1260 /* Notify application */
1264 silc_client_command_free(cmd);
1267 /* OPER command. Used to obtain server operator privileges. */
1269 SILC_CLIENT_CMD_FUNC(oper)
1271 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1272 SilcClientConnection conn = cmd->conn;
1274 unsigned char *auth_data;
1278 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1283 if (cmd->argc < 2) {
1284 cmd->client->ops->say(cmd->client, conn,
1285 "Usage: /OPER <username> [<public key>]");
1290 if (cmd->argc == 3) {
1291 /* XXX Get public key */
1296 /* Get passphrase */
1298 auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1304 /* Encode the authentication payload */
1305 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1306 auth_data, strlen(auth_data));
1309 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1311 strlen(cmd->argv[1]),
1312 2, auth->data, auth->len);
1313 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1314 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1316 silc_buffer_free(buffer);
1317 silc_buffer_free(auth);
1318 memset(auth_data, 0, strlen(auth_data));
1319 silc_free(auth_data);
1321 /* Notify application */
1325 silc_client_command_free(cmd);
1328 /* SILCOPER command. Used to obtain router operator privileges. */
1330 SILC_CLIENT_CMD_FUNC(silcoper)
1332 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1333 SilcClientConnection conn = cmd->conn;
1335 unsigned char *auth_data;
1339 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1344 if (cmd->argc < 2) {
1345 cmd->client->ops->say(cmd->client, conn,
1346 "Usage: /SILCOPER <username> [<public key>]");
1351 if (cmd->argc == 3) {
1352 /* XXX Get public key */
1357 /* Get passphrase */
1359 auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1365 /* Encode the authentication payload */
1366 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1367 auth_data, strlen(auth_data));
1370 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1372 strlen(cmd->argv[1]),
1373 2, auth->data, auth->len);
1374 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1375 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1377 silc_buffer_free(buffer);
1378 silc_buffer_free(auth);
1379 memset(auth_data, 0, strlen(auth_data));
1380 silc_free(auth_data);
1382 /* Notify application */
1386 silc_client_command_free(cmd);
1389 /* CONNECT command. Connects the server to another server. */
1391 SILC_CLIENT_CMD_FUNC(connect)
1393 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1394 SilcClientConnection conn = cmd->conn;
1396 unsigned char port[4];
1400 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1405 if (cmd->argc < 2) {
1406 cmd->client->ops->say(cmd->client, conn,
1407 "Usage: /CONNECT <server> [<port>]");
1412 if (cmd->argc == 3) {
1413 tmp = atoi(cmd->argv[2]);
1414 SILC_PUT32_MSB(tmp, port);
1418 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1420 strlen(cmd->argv[1]),
1423 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1425 strlen(cmd->argv[1]));
1426 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1427 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1428 silc_buffer_free(buffer);
1430 /* Notify application */
1434 silc_client_command_free(cmd);
1437 SILC_CLIENT_CMD_FUNC(restart)
1441 /* CLOSE command. Close server connection to the remote server */
1443 SILC_CLIENT_CMD_FUNC(close)
1445 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1446 SilcClientConnection conn = cmd->conn;
1448 unsigned char port[4];
1452 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1457 if (cmd->argc < 2) {
1458 cmd->client->ops->say(cmd->client, conn,
1459 "Usage: /CLOSE <server> [<port>]");
1464 if (cmd->argc == 3) {
1465 tmp = atoi(cmd->argv[2]);
1466 SILC_PUT32_MSB(tmp, port);
1470 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1472 strlen(cmd->argv[1]),
1475 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1477 strlen(cmd->argv[1]));
1478 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1479 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1480 silc_buffer_free(buffer);
1482 /* Notify application */
1486 silc_client_command_free(cmd);
1489 /* SHUTDOWN command. Shutdowns the server. */
1491 SILC_CLIENT_CMD_FUNC(shutdown)
1493 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1496 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1501 /* Send the command */
1502 silc_client_send_command(cmd->client, cmd->conn,
1503 SILC_COMMAND_SHUTDOWN, 0, 0);
1505 /* Notify application */
1509 silc_client_command_free(cmd);
1512 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1514 SILC_CLIENT_CMD_FUNC(leave)
1516 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1517 SilcClientConnection conn = cmd->conn;
1518 SilcIDCacheEntry id_cache = NULL;
1519 SilcChannelEntry channel;
1520 SilcBuffer buffer, idp;
1524 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1529 if (cmd->argc != 2) {
1530 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1535 if (cmd->argv[1][0] == '*') {
1536 if (!conn->current_channel) {
1537 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1541 name = conn->current_channel->channel_name;
1543 name = cmd->argv[1];
1546 if (!conn->current_channel) {
1547 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1552 /* Get the Channel ID of the channel */
1553 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1554 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1559 channel = (SilcChannelEntry)id_cache->context;
1561 /* Send LEAVE command to the server */
1562 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1563 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1564 1, idp->data, idp->len);
1565 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1566 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1567 silc_buffer_free(buffer);
1568 silc_buffer_free(idp);
1570 /* We won't talk anymore on this channel */
1571 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1573 conn->current_channel = NULL;
1575 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1576 silc_free(channel->channel_name);
1577 silc_free(channel->id);
1578 silc_free(channel->key);
1579 silc_cipher_free(channel->channel_key);
1582 /* Notify application */
1586 silc_client_command_free(cmd);
1589 /* Command USERS. Requests the USERS of the clients joined on requested
1592 SILC_CLIENT_CMD_FUNC(users)
1594 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1595 SilcClientConnection conn = cmd->conn;
1596 SilcIDCacheEntry id_cache = NULL;
1597 SilcChannelEntry channel;
1598 SilcBuffer buffer, idp;
1599 char *name, *line = NULL;
1600 unsigned int line_len = 0;
1603 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1608 if (cmd->argc != 2) {
1609 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1614 if (cmd->argv[1][0] == '*') {
1615 if (!conn->current_channel) {
1616 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1620 name = conn->current_channel->channel_name;
1622 name = cmd->argv[1];
1625 if (!conn->current_channel) {
1626 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1631 /* Get the Channel ID of the channel */
1632 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1633 /* XXX should resolve the channel ID; LIST command */
1634 cmd->client->ops->say(cmd->client, conn,
1635 "You are not on that channel", name);
1640 channel = (SilcChannelEntry)id_cache->context;
1642 if (!cmd->pending) {
1643 /* Send USERS command to the server */
1644 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1645 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 0, 1,
1646 1, idp->data, idp->len);
1647 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1648 NULL, 0, NULL, NULL, buffer->data,
1650 silc_buffer_free(buffer);
1651 silc_buffer_free(idp);
1653 /* Register pending callback which will recall this command callback with
1654 same context and reprocesses the command. When reprocessing we actually
1655 display the information on the screen. */
1656 silc_client_command_pending(conn, SILC_COMMAND_USERS, 0,
1657 silc_client_command_destructor,
1658 silc_client_command_users,
1659 silc_client_command_dup(cmd));
1660 cmd->pending = TRUE;
1665 /* Pending command. Now we've resolved the information from server and
1666 we are ready to display the information on screen. */
1668 SilcChannelUser chu;
1670 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1671 channel->channel_name);
1673 line = silc_calloc(4096, sizeof(*line));
1675 silc_list_start(channel->clients);
1676 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1677 SilcClientEntry e = chu->client;
1678 char *m, tmp[80], len1;
1680 memset(line, 0, sizeof(line_len));
1682 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1684 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1685 line = silc_calloc(line_len, sizeof(*line));
1688 memset(tmp, 0, sizeof(tmp));
1689 m = silc_client_chumode_char(chu->mode);
1691 strncat(line, " ", 1);
1692 strncat(line, e->nickname, strlen(e->nickname));
1693 strncat(line, e->server ? "@" : "", 1);
1697 len1 = strlen(e->server);
1698 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1700 len1 = strlen(line);
1702 memset(&line[29], 0, len1 - 29);
1704 for (i = 0; i < 30 - len1 - 1; i++)
1708 strncat(line, " H", 3);
1709 strcat(tmp, m ? m : "");
1710 strncat(line, tmp, strlen(tmp));
1712 if (strlen(tmp) < 5)
1713 for (i = 0; i < 5 - strlen(tmp); i++)
1716 strcat(line, e->username ? e->username : "");
1718 cmd->client->ops->say(cmd->client, conn, "%s", line);
1728 /* Notify application */
1732 silc_client_command_free(cmd);