5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 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; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 /* Command operation that is called at the end of all commands.
28 Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command( \
30 cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status) \
34 cmd->conn->client->internal->ops->command(cmd->conn->client, \
35 cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args) \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name, \
40 silc_client_command_##func, \
41 silc_client_command_reply_##func, args)
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name) \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd, \
46 silc_client_command_##func, \
47 silc_client_command_reply_##func)
49 #define SAY cmd->conn->client->internal->ops->say
51 /************************ Static utility functions **************************/
53 /* Return next available command identifier. */
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
59 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
61 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
66 /* State to finish command thread after an error in resolving command */
68 SILC_FSM_STATE(silc_client_command_continue_error)
70 /* Destructor will free all resources */
71 return SILC_FSM_FINISH;
74 /* Command reply callback to continue with the execution of a command.
75 This will continue when first successful reply is received, and ignores
76 the rest. On the other hand, if only errors are received it will
77 wait for all errors before continuing. */
79 static SilcBool silc_client_command_continue(SilcClient client,
80 SilcClientConnection conn,
87 SilcClientCommandContext cmd = context;
89 /* Continue immediately when successful reply is received */
90 if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91 SILC_FSM_CALL_CONTINUE(&cmd->thread);
98 /* Continue after last error is received */
99 if (SILC_STATUS_IS_ERROR(status) ||
100 (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102 SILC_FSM_CALL_CONTINUE(&cmd->thread);
109 /* Continues after resolving completed. */
111 static void silc_client_command_resolve_continue(SilcClient client,
112 SilcClientConnection conn,
117 SilcClientCommandContext cmd = context;
119 if (status != SILC_STATUS_OK)
120 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
122 /* Continue with the command */
123 SILC_FSM_CALL_CONTINUE(&cmd->thread);
126 /* Dummy command callback. Nothing interesting to do here. Use this when
127 you just send command but don't care about reply. */
129 SilcBool silc_client_command_called_dummy(SilcClient client,
130 SilcClientConnection conn,
140 /* Dummy resolving callback. Nothing interesting to do here. Use this
141 when you just resolve entires but don't care about reply. */
143 void silc_client_command_resolve_dummy(SilcClient client,
144 SilcClientConnection conn,
152 /* Register command to client */
155 silc_client_command_register(SilcClient client,
158 SilcFSMStateCallback command_func,
159 SilcFSMStateCallback command_reply_func,
162 SilcClientCommand cmd;
164 cmd = silc_calloc(1, sizeof(*cmd));
168 cmd->command = command_func;
169 cmd->reply = command_reply_func;
170 cmd->max_args = max_args;
171 cmd->name = name ? strdup(name) : NULL;
177 silc_list_add(client->internal->commands, cmd);
182 /* Unregister command from client */
185 silc_client_command_unregister(SilcClient client,
187 SilcFSMStateCallback command_func,
188 SilcFSMStateCallback command_reply_func)
190 SilcClientCommand cmd;
192 silc_list_start(client->internal->commands);
193 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
194 if (cmd->cmd == command && cmd->command == command_func &&
195 cmd->reply == command_reply_func) {
196 silc_list_del(client->internal->commands, cmd);
197 silc_free(cmd->name);
206 /* Finds and returns a pointer to the command list. Return NULL if the
207 command is not found. */
209 static SilcClientCommand silc_client_command_find(SilcClient client,
212 SilcClientCommand cmd;
214 silc_list_start(client->internal->commands);
215 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
216 if (cmd->name && !strcasecmp(cmd->name, name))
223 /* Command thread destructor */
225 static void silc_client_command_destructor(SilcFSMThread thread,
227 void *destructor_context)
229 SilcClientCommandContext cmd = fsm_context;
230 SilcClientConnection conn = cmd->conn;
232 /* Removes commands that aren't waiting for reply but are waiting
233 for something. They may not have been removed yet. */
234 silc_list_del(conn->internal->pending_commands, cmd);
236 silc_client_command_free(cmd);
239 /* Add a command pending a command reply. Used internally by the library. */
242 silc_client_command_add_pending(SilcClientConnection conn,
243 SilcClientCommandContext cmd,
244 SilcClientCommandReply reply,
247 SilcClientCommandReplyCallback cb;
249 silc_mutex_lock(conn->internal->lock);
251 /* Add pending callback, if defined */
253 cb = silc_calloc(1, sizeof(*cb));
255 silc_mutex_unlock(conn->internal->lock);
259 cb->context = context;
260 silc_list_add(cmd->reply_callbacks, cb);
263 /* Add pending reply */
264 silc_list_add(conn->internal->pending_commands, cmd);
266 silc_mutex_unlock(conn->internal->lock);
271 /* Generic function to send any command. The arguments must be sent already
272 encoded into correct format and in correct order. Arguments come from
273 variable argument list pointer. */
275 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
276 SilcClientConnection conn,
277 SilcClientCommandContext cmd,
279 SilcClientCommandReply reply,
281 SilcUInt32 argc, va_list ap)
285 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
287 if (conn->internal->disconnected)
291 cmd->cmd_ident = silc_client_cmd_ident(conn);
293 /* Encode command payload */
294 packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
298 /* Send the command */
299 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
300 silc_buffer_datalen(packet))) {
301 silc_buffer_free(packet);
305 /* Add the command pending command reply */
306 silc_client_command_add_pending(conn, cmd, reply, reply_context);
308 silc_buffer_free(packet);
310 return cmd->cmd_ident;
313 /* Generic function to send any command. The arguments must be sent already
314 encoded into correct format and in correct order. Arguments come from
318 silc_client_command_send_arg_array(SilcClient client,
319 SilcClientConnection conn,
320 SilcClientCommandContext cmd,
322 SilcClientCommandReply reply,
325 unsigned char **argv,
326 SilcUInt32 *argv_lens,
327 SilcUInt32 *argv_types)
331 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
333 if (conn->internal->disconnected)
337 cmd->cmd_ident = silc_client_cmd_ident(conn);
339 /* Encode command payload */
340 packet = silc_command_payload_encode(command, argc, argv, argv_lens,
341 argv_types, cmd->cmd_ident);
345 /* Send the command */
346 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
347 silc_buffer_datalen(packet))) {
348 silc_buffer_free(packet);
352 /* Add the command pending command reply */
353 silc_client_command_add_pending(conn, cmd, reply, reply_context);
355 silc_buffer_free(packet);
357 return cmd->cmd_ident;
360 /* Generic function to send any command. The arguments must be sent already
361 encoded into correct format and in correct order. This is used internally
364 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
365 SilcClientCommandContext cmd,
367 SilcClientCommandReply reply,
369 SilcUInt32 argc, ...)
372 SilcUInt16 cmd_ident;
375 cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
376 reply, reply_context, argc, ap);
382 /****************************** Command API *********************************/
384 /* Free command context and its internals */
386 void silc_client_command_free(SilcClientCommandContext cmd)
388 SilcClientCommandReplyCallback cb;
391 for (i = 0; i < cmd->argc; i++)
392 silc_free(cmd->argv[i]);
393 silc_free(cmd->argv);
394 silc_free(cmd->argv_lens);
395 silc_free(cmd->argv_types);
397 silc_list_start(cmd->reply_callbacks);
398 while ((cb = silc_list_get(cmd->reply_callbacks)))
404 /* Executes a command */
406 SilcUInt16 silc_client_command_call(SilcClient client,
407 SilcClientConnection conn,
408 const char *command_line, ...)
412 unsigned char **argv = NULL;
413 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
414 SilcClientCommand command;
415 SilcClientCommandContext cmd;
419 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
420 "You are not connected to a server, please connect to server");
424 /* Parse arguments */
425 va_start(va, command_line);
429 /* Get command name */
430 command_name = silc_memdup(command_line, strcspn(command_line, " "));
434 /* Find command by name */
435 command = silc_client_command_find(client, command_name);
437 silc_free(command_name);
441 /* Parse command line */
442 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
443 &argv_types, &argc, command->max_args);
445 silc_free(command_name);
447 arg = va_arg(va, char *);
451 /* Find command by name */
452 command = silc_client_command_find(client, arg);
457 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
458 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
459 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
460 if (!argv || !argv_lens || !argv_types)
462 argv[argc] = silc_memdup(arg, strlen(arg));
465 argv_lens[argc] = strlen(arg);
466 argv_types[argc] = argc;
468 arg = va_arg(va, char *);
473 /* Allocate command context */
474 cmd = silc_calloc(1, sizeof(*cmd));
478 cmd->cmd = command->cmd;
481 cmd->argv_lens = argv_lens;
482 cmd->argv_types = argv_types;
483 cmd->cmd_ident = silc_client_cmd_ident(conn);
486 silc_list_init(cmd->reply_callbacks,
487 struct SilcClientCommandReplyCallbackStruct, next);
490 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
491 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
492 silc_client_command_destructor, NULL, FALSE);
493 silc_fsm_start_sync(&cmd->thread, command->command);
495 return cmd->cmd_ident;
498 /* Generic function to send any command. The arguments must be sent already
499 encoded into correct format and in correct order. */
501 SilcUInt16 silc_client_command_send(SilcClient client,
502 SilcClientConnection conn,
504 SilcClientCommandReply reply,
506 SilcUInt32 argc, ...)
508 SilcClientCommandContext cmd;
514 /* Allocate command context */
515 cmd = silc_calloc(1, sizeof(*cmd));
520 silc_list_init(cmd->reply_callbacks,
521 struct SilcClientCommandReplyCallbackStruct, next);
523 /* Send the command */
526 silc_client_command_send_vap(client, conn, cmd, command, reply,
527 reply_context, argc, ap);
530 if (!cmd->cmd_ident) {
531 silc_client_command_free(cmd);
535 /*** Wait for command reply */
536 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
537 silc_client_command_destructor, NULL, FALSE);
538 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
540 return cmd->cmd_ident;
543 /* Generic function to send any command. The arguments must be sent already
544 encoded into correct format and in correct order. Arguments come from
547 SilcUInt16 silc_client_command_send_argv(SilcClient client,
548 SilcClientConnection conn,
550 SilcClientCommandReply reply,
553 unsigned char **argv,
554 SilcUInt32 *argv_lens,
555 SilcUInt32 *argv_types)
557 SilcClientCommandContext cmd;
562 /* Allocate command context */
563 cmd = silc_calloc(1, sizeof(*cmd));
569 /* Send the command */
571 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
572 reply_context, argc, argv, argv_lens,
574 if (!cmd->cmd_ident) {
575 silc_client_command_free(cmd);
579 /*** Wait for command reply */
580 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
581 silc_client_command_destructor, NULL, FALSE);
582 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
584 return cmd->cmd_ident;
587 /* Attach to a command and command identifier to receive command reply. */
589 SilcBool silc_client_command_pending(SilcClientConnection conn,
592 SilcClientCommandReply reply,
595 SilcClientCommandContext cmd;
596 SilcClientCommandReplyCallback cb;
601 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
603 silc_mutex_lock(conn->internal->lock);
605 /* Find the pending command */
606 silc_list_start(conn->internal->pending_commands);
607 while ((cmd = silc_list_get(conn->internal->pending_commands)))
608 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
609 && cmd->cmd_ident == ident) {
611 /* Add the callback */
612 cb = silc_calloc(1, sizeof(*cb));
616 cb->context = context;
617 silc_list_add(cmd->reply_callbacks, cb);
620 silc_mutex_unlock(conn->internal->lock);
625 /******************************** WHOIS *************************************/
627 /* Command WHOIS. This command is used to query information about
630 SILC_FSM_STATE(silc_client_command_whois)
632 SilcClientCommandContext cmd = fsm_context;
633 SilcClientConnection conn = cmd->conn;
634 SilcClient client = conn->client;
635 SilcBuffer attrs = NULL;
636 unsigned char count[4], *tmp = NULL;
637 SilcBool details = FALSE, nick = FALSE;
638 unsigned char *pubkey = NULL;
641 /* Given without arguments fetches client's own information */
643 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
644 silc_buffer_data(conn->internal->local_idp),
645 silc_buffer_len(conn->internal->local_idp));
647 /* Notify application */
648 COMMAND(SILC_STATUS_OK);
650 /** Wait for command reply */
651 silc_fsm_next(fsm, silc_client_command_reply_wait);
652 return SILC_FSM_CONTINUE;
655 for (i = 1; i < cmd->argc; i++) {
656 if (!strcasecmp(cmd->argv[i], "-details")) {
658 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
659 pubkey = cmd->argv[i + 1];
662 /* We assume that the first parameter is the nickname, if it isn't
663 -details or -pubkey. The last parameter should always be the count */
666 } else if (i == cmd->argc - 1) {
667 int c = atoi(cmd->argv[i]);
668 SILC_PUT32_MSB(c, count);
675 /* If pubkey is set, add all attributes to the attrs buffer, except
678 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
679 SILC_ATTRIBUTE_SERVICE,
680 SILC_ATTRIBUTE_STATUS_MOOD,
681 SILC_ATTRIBUTE_STATUS_FREETEXT,
682 SILC_ATTRIBUTE_STATUS_MESSAGE,
683 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
684 SILC_ATTRIBUTE_PREFERRED_CONTACT,
685 SILC_ATTRIBUTE_TIMEZONE,
686 SILC_ATTRIBUTE_GEOLOCATION,
687 SILC_ATTRIBUTE_DEVICE_INFO,
688 SILC_ATTRIBUTE_USER_ICON, 0);
690 attrs = silc_client_attributes_request(0);
695 SilcAttributeObjPk obj;
698 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
699 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
700 "Could not load public key %s, check the filename",
702 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
706 switch (silc_pkcs_get_type(pk)) {
708 obj.type = "silc-rsa";
711 obj.type = "ssh-rsa";
713 case SILC_PKCS_X509V3:
714 obj.type = "x509v3-sign-rsa";
716 case SILC_PKCS_OPENPGP:
717 obj.type = "pgp-sign-rsa";
723 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
725 attrs = silc_attribute_payload_encode(attrs,
726 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
727 SILC_ATTRIBUTE_FLAG_VALID,
732 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
733 3, 1, nick ? cmd->argv[1] : NULL,
734 nick ? cmd->argv_lens[1] : 0,
735 2, tmp ? tmp : NULL, tmp ? 4 : 0,
736 3, silc_buffer_datalen(attrs));
738 /* Notify application */
739 COMMAND(SILC_STATUS_OK);
741 /** Wait for command reply */
742 silc_fsm_next(fsm, silc_client_command_reply_wait);
743 return SILC_FSM_CONTINUE;
746 return SILC_FSM_FINISH;
749 /******************************** WHOWAS ************************************/
751 /* Command WHOWAS. This command is used to query history information about
752 specific user that used to exist in the network. */
754 SILC_FSM_STATE(silc_client_command_whowas)
756 SilcClientCommandContext cmd = fsm_context;
757 SilcClientConnection conn = cmd->conn;
758 unsigned char count[4];
761 if (cmd->argc < 2 || cmd->argc > 3) {
762 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
763 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
764 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
765 SILC_STATUS_ERR_TOO_MANY_PARAMS));
766 return SILC_FSM_FINISH;
769 if (cmd->argc == 2) {
770 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
771 1, 1, cmd->argv[1], cmd->argv_lens[1]);
773 c = atoi(cmd->argv[2]);
774 SILC_PUT32_MSB(c, count);
775 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
776 2, 1, cmd->argv[1], cmd->argv_lens[1],
777 2, count, sizeof(count));
780 /* Notify application */
781 COMMAND(SILC_STATUS_OK);
783 /** Wait for command reply */
784 silc_fsm_next(fsm, silc_client_command_reply_wait);
785 return SILC_FSM_CONTINUE;
788 /******************************** IDENTIFY **********************************/
790 /* Command IDENTIFY. This command is used to query information about
791 specific user, especially ID's. */
793 SILC_FSM_STATE(silc_client_command_identify)
795 SilcClientCommandContext cmd = fsm_context;
796 SilcClientConnection conn = cmd->conn;
797 unsigned char count[4];
800 if (cmd->argc < 2 || cmd->argc > 3)
801 return SILC_FSM_FINISH;
803 if (cmd->argc == 2) {
804 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
805 1, 1, cmd->argv[1], cmd->argv_lens[1]);
807 c = atoi(cmd->argv[2]);
808 SILC_PUT32_MSB(c, count);
809 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
810 2, 1, cmd->argv[1], cmd->argv_lens[1],
811 4, count, sizeof(count));
814 /** Wait for command reply */
815 silc_fsm_next(fsm, silc_client_command_reply_wait);
816 return SILC_FSM_CONTINUE;
819 /********************************** NICK ************************************/
821 /* Command NICK. Shows current nickname/sets new nickname on current
824 SILC_FSM_STATE(silc_client_command_nick)
826 SilcClientCommandContext cmd2, cmd = fsm_context;
827 SilcClientConnection conn = cmd->conn;
830 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
831 "Usage: /NICK <nickname>");
832 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
836 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
839 /* Show current nickname */
842 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
843 "Your nickname is %s on server %s",
844 conn->local_entry->nickname, conn->remote_host);
846 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
847 "Your nickname is %s", conn->local_entry->nickname);
850 COMMAND(SILC_STATUS_OK);
854 /* If JOIN command is active, wait for it to finish before sending NICK.
855 To avoid problems locally with changing IDs while joining, we do this. */
856 silc_mutex_lock(conn->internal->lock);
857 silc_list_start(conn->internal->pending_commands);
858 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
859 if (cmd2->cmd == SILC_COMMAND_JOIN) {
860 silc_mutex_unlock(conn->internal->lock);
861 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
862 return SILC_FSM_WAIT;
865 silc_mutex_unlock(conn->internal->lock);
867 if (cmd->argv_lens[1] > 128)
868 cmd->argv_lens[1] = 128;
870 /* Send the NICK command */
871 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
872 1, 1, cmd->argv[1], cmd->argv_lens[1]);
874 /* Notify application */
875 COMMAND(SILC_STATUS_OK);
877 /** Wait for command reply */
878 silc_fsm_next(fsm, silc_client_command_reply_wait);
879 return SILC_FSM_CONTINUE;
882 return SILC_FSM_FINISH;
885 /********************************** LIST ************************************/
887 /* Command LIST. Lists channels on the current server. */
889 SILC_FSM_STATE(silc_client_command_list)
891 SilcClientCommandContext cmd = fsm_context;
892 SilcClientConnection conn = cmd->conn;
893 SilcClient client = conn->client;
894 SilcChannelEntry channel = NULL;
895 SilcBuffer idp = NULL;
897 if (cmd->argc == 2) {
898 /* Get the Channel ID of the channel */
899 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
901 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
905 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
907 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
908 1, 1, silc_buffer_datalen(idp));
910 silc_buffer_free(idp);
911 silc_client_unref_channel(client, conn, channel);
913 /* Notify application */
914 COMMAND(SILC_STATUS_OK);
916 /** Wait for command reply */
917 silc_fsm_next(fsm, silc_client_command_reply_wait);
918 return SILC_FSM_CONTINUE;
921 /********************************** TOPIC ***********************************/
923 /* Command TOPIC. Sets/shows topic on a channel. */
925 SILC_FSM_STATE(silc_client_command_topic)
927 SilcClientCommandContext cmd = fsm_context;
928 SilcClientConnection conn = cmd->conn;
929 SilcClient client = conn->client;
930 SilcChannelEntry channel;
934 if (cmd->argc < 2 || cmd->argc > 3) {
935 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
936 "Usage: /TOPIC <channel> [<topic>]");
937 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
938 SILC_STATUS_ERR_TOO_MANY_PARAMS));
942 if (cmd->argv[1][0] == '*') {
943 if (!conn->current_channel) {
944 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
947 name = conn->current_channel->channel_name;
952 if (!conn->current_channel) {
953 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
957 /* Get the Channel ID of the channel */
958 channel = silc_client_get_channel(conn->client, conn, name);
960 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
964 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
966 /* Send TOPIC command to the server */
968 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
969 1, silc_buffer_datalen(idp),
970 2, cmd->argv[2], strlen(cmd->argv[2]));
972 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
973 1, silc_buffer_datalen(idp));
975 silc_buffer_free(idp);
976 silc_client_unref_channel(client, conn, channel);
978 /* Notify application */
979 COMMAND(SILC_STATUS_OK);
981 /** Wait for command reply */
982 silc_fsm_next(fsm, silc_client_command_reply_wait);
983 return SILC_FSM_CONTINUE;
986 return SILC_FSM_FINISH;
989 /********************************* INVITE ***********************************/
991 /* Command INVITE. Invites specific client to join a channel. This is
992 also used to mange the invite list of the channel. */
994 SILC_FSM_STATE(silc_client_command_invite)
996 SilcClientCommandContext cmd = fsm_context;
997 SilcClientConnection conn = cmd->conn;
998 SilcClient client = conn->client;
999 SilcClientEntry client_entry = NULL;
1000 SilcChannelEntry channel = NULL;
1001 SilcBuffer clidp, chidp, args = NULL;
1002 SilcPublicKey pubkey = NULL;
1003 SilcDList clients = NULL;
1004 char *nickname = NULL, *name;
1005 char *invite = NULL;
1006 unsigned char action[1];
1008 if (cmd->argc < 2) {
1009 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1010 "Usage: /INVITE <channel> [<nickname>[@server>]"
1011 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1012 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1016 if (cmd->argv[1][0] == '*') {
1017 if (!conn->current_channel) {
1018 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1022 channel = conn->current_channel;
1023 silc_client_ref_channel(client, conn, channel);
1025 name = cmd->argv[1];
1027 channel = silc_client_get_channel(conn->client, conn, name);
1029 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1034 /* Parse the typed nickname. */
1035 if (cmd->argc == 3) {
1036 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1037 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1039 /* Find client entry */
1040 clients = silc_client_get_clients_local(client, conn, nickname,
1043 /* Resolve client information */
1044 SILC_FSM_CALL(silc_client_get_clients(
1045 client, conn, nickname,
1047 silc_client_command_resolve_continue,
1050 client_entry = silc_dlist_get(clients);
1052 if (cmd->argv[2][0] == '+')
1057 /* Check if it is public key file to be added to invite list */
1058 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1059 invite = cmd->argv[2];
1066 args = silc_buffer_alloc_size(2);
1067 silc_buffer_format(args,
1068 SILC_STR_UI_SHORT(1),
1071 chidp = silc_public_key_payload_encode(pubkey);
1072 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1073 silc_buffer_len(chidp), 2);
1074 silc_buffer_free(chidp);
1075 silc_pkcs_public_key_free(pubkey);
1077 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1081 /* Send the command */
1082 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1084 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1085 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1086 1, silc_buffer_datalen(chidp),
1087 2, silc_buffer_datalen(clidp),
1088 3, args ? action : NULL, args ? 1 : 0,
1089 4, silc_buffer_datalen(args));
1090 silc_buffer_free(clidp);
1092 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1093 1, silc_buffer_datalen(chidp),
1094 3, args ? action : NULL, args ? 1 : 0,
1095 4, silc_buffer_datalen(args));
1098 silc_buffer_free(chidp);
1099 silc_buffer_free(args);
1100 silc_free(nickname);
1101 silc_client_list_free(client, conn, clients);
1102 silc_client_unref_channel(client, conn, channel);
1104 /* Notify application */
1105 COMMAND(SILC_STATUS_OK);
1107 /** Wait for command reply */
1108 silc_fsm_next(fsm, silc_client_command_reply_wait);
1109 return SILC_FSM_CONTINUE;
1112 silc_free(nickname);
1113 return SILC_FSM_FINISH;
1116 /********************************** QUIT ************************************/
1118 /* Close the connection */
1120 SILC_FSM_STATE(silc_client_command_quit_final)
1122 SilcClientCommandContext cmd = fsm_context;
1123 SilcClientConnection conn = cmd->conn;
1125 SILC_LOG_DEBUG(("Quitting"));
1127 /* Notify application */
1128 COMMAND(SILC_STATUS_OK);
1130 /* Signal to close connection */
1131 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1132 if (!conn->internal->disconnected) {
1133 conn->internal->disconnected = TRUE;
1134 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1137 return SILC_FSM_FINISH;
1140 /* Command QUIT. Closes connection with current server. */
1142 SILC_FSM_STATE(silc_client_command_quit)
1144 SilcClientCommandContext cmd = fsm_context;
1145 SilcClientConnection conn = cmd->conn;
1148 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1149 1, cmd->argv[1], cmd->argv_lens[1]);
1151 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1153 /* Sleep for a while */
1156 /* We close the connection with a little timeout */
1157 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1158 return SILC_FSM_WAIT;
1161 /********************************** KILL ************************************/
1163 /* Command KILL. Router operator can use this command to remove an client
1164 fromthe SILC Network. */
1166 SILC_FSM_STATE(silc_client_command_kill)
1168 SilcClientCommandContext cmd = fsm_context;
1169 SilcClientConnection conn = cmd->conn;
1170 SilcClient client = conn->client;
1171 SilcBuffer idp, auth = NULL;
1172 SilcClientEntry target;
1174 char *nickname = NULL, *comment = NULL;
1176 if (cmd->argc < 2) {
1177 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1178 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1179 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1180 return SILC_FSM_FINISH;
1183 /* Parse the typed nickname. */
1184 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1185 return SILC_FSM_FINISH;
1187 /* Get the target client */
1188 clients = silc_client_get_clients_local(client, conn, nickname,
1191 /* Resolve client information */
1192 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
1194 silc_client_command_resolve_continue,
1197 target = silc_dlist_get(clients);
1199 if (cmd->argc >= 3) {
1200 if (strcasecmp(cmd->argv[2], "-pubkey"))
1201 comment = cmd->argv[2];
1203 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1204 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1205 /* Encode the public key authentication payload */
1206 auth = silc_auth_public_key_auth_generate(conn->public_key,
1209 conn->internal->sha1hash,
1210 &target->id, SILC_ID_CLIENT);
1214 /* Send the KILL command to the server */
1215 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1216 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1217 1, silc_buffer_datalen(idp),
1218 2, comment, comment ? strlen(comment) : 0,
1219 3, silc_buffer_datalen(auth));
1220 silc_buffer_free(idp);
1221 silc_buffer_free(auth);
1222 silc_free(nickname);
1223 silc_client_list_free(client, conn, clients);
1225 /* Notify application */
1226 COMMAND(SILC_STATUS_OK);
1228 /** Wait for command reply */
1229 silc_fsm_next(fsm, silc_client_command_reply_wait);
1230 return SILC_FSM_CONTINUE;
1233 /********************************** INFO ************************************/
1235 /* Command INFO. Request information about specific server. If specific
1236 server is not provided the current server is used. */
1238 SILC_FSM_STATE(silc_client_command_info)
1240 SilcClientCommandContext cmd = fsm_context;
1241 SilcClientConnection conn = cmd->conn;
1243 /* Send the command */
1245 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1246 1, cmd->argv[1], cmd->argv_lens[1]);
1248 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1250 /* Notify application */
1251 COMMAND(SILC_STATUS_OK);
1253 /** Wait for command reply */
1254 silc_fsm_next(fsm, silc_client_command_reply_wait);
1255 return SILC_FSM_CONTINUE;
1258 /********************************** STATS ***********************************/
1260 /* Command STATS. Shows server and network statistics. */
1262 SILC_FSM_STATE(silc_client_command_stats)
1264 SilcClientCommandContext cmd = fsm_context;
1265 SilcClientConnection conn = cmd->conn;
1267 /* Send the command */
1268 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1269 1, silc_buffer_datalen(conn->internal->
1272 /* Notify application */
1273 COMMAND(SILC_STATUS_OK);
1275 /** Wait for command reply */
1276 silc_fsm_next(fsm, silc_client_command_reply_wait);
1277 return SILC_FSM_CONTINUE;
1280 /********************************** PING ************************************/
1282 /* Command PING. Sends ping to server. */
1284 SILC_FSM_STATE(silc_client_command_ping)
1286 SilcClientCommandContext cmd = fsm_context;
1287 SilcClientConnection conn = cmd->conn;
1289 if (cmd->argc < 2) {
1290 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1291 return SILC_FSM_FINISH;
1294 /* Send the command */
1295 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1296 1, silc_buffer_datalen(conn->internal->
1299 /* Save ping time */
1300 cmd->context = SILC_64_TO_PTR(silc_time());
1302 /* Notify application */
1303 COMMAND(SILC_STATUS_OK);
1305 /** Wait for command reply */
1306 silc_fsm_next(fsm, silc_client_command_reply_wait);
1307 return SILC_FSM_CONTINUE;
1310 /********************************** JOIN ************************************/
1312 /* Command JOIN. Joins to a channel. */
1314 SILC_FSM_STATE(silc_client_command_join)
1316 SilcClientCommandContext cmd2, cmd = fsm_context;
1317 SilcClientConnection conn = cmd->conn;
1318 SilcClient client = conn->client;
1319 SilcChannelEntry channel = NULL;
1320 SilcBuffer auth = NULL, cauth = NULL;
1321 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1322 int i, passphrase_len = 0;
1324 if (cmd->argc < 2) {
1325 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1329 /* See if we have joined to the requested channel already */
1330 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1331 if (channel && silc_client_on_channel(channel, conn->local_entry))
1334 /* If NICK command is active, wait for it to finish before sending JOIN.
1335 To avoid problems locally with changing IDs while joining, we do this. */
1336 silc_mutex_lock(conn->internal->lock);
1337 silc_list_start(conn->internal->pending_commands);
1338 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1339 if (cmd2->cmd == SILC_COMMAND_NICK) {
1340 silc_mutex_unlock(conn->internal->lock);
1341 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1342 return SILC_FSM_WAIT;
1345 silc_mutex_unlock(conn->internal->lock);
1347 if (cmd->argv_lens[1] > 256)
1348 cmd->argv_lens[1] = 256;
1350 name = cmd->argv[1];
1352 for (i = 2; i < cmd->argc; i++) {
1353 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1354 cipher = cmd->argv[++i];
1355 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1356 hmac = cmd->argv[++i];
1357 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1358 auth = silc_auth_public_key_auth_generate(conn->public_key,
1361 conn->internal->sha1hash,
1364 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1365 SilcPublicKey pubkey = conn->public_key;
1366 SilcPrivateKey privkey = conn->private_key;
1367 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1370 if (cmd->argc >= i + 3) {
1372 if (cmd->argc >= i + 4) {
1373 pass = cmd->argv[i + 3];
1376 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1377 &pubkey, &privkey)) {
1378 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1379 "Could not load key pair, check your arguments");
1380 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1386 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1387 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1389 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1390 memcpy(pubdata, pkhash, 20);
1391 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1393 conn->internal->sha1hash,
1396 memset(pubdata, 0, 128);
1399 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1400 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1401 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1402 cmd->argv_lens[i], 0);
1403 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1404 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1405 0, pu8, passphrase_len);
1408 passphrase = strdup(cmd->argv[i]);
1409 passphrase_len = cmd->argv_lens[i];
1414 /* Send JOIN command to the server */
1415 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1416 1, name, strlen(name),
1417 2, silc_buffer_datalen(conn->internal->
1419 3, passphrase, passphrase_len,
1420 4, cipher, cipher ? strlen(cipher) : 0,
1421 5, hmac, hmac ? strlen(hmac) : 0,
1422 6, silc_buffer_datalen(auth),
1423 7, silc_buffer_datalen(cauth));
1425 silc_buffer_free(auth);
1426 silc_buffer_free(cauth);
1428 memset(passphrase, 0, strlen(passphrase));
1429 silc_free(passphrase);
1430 silc_client_unref_channel(client, conn, channel);
1432 /* Notify application */
1433 COMMAND(SILC_STATUS_OK);
1435 /** Wait for command reply */
1436 silc_fsm_next(fsm, silc_client_command_reply_wait);
1437 return SILC_FSM_CONTINUE;
1440 silc_client_unref_channel(client, conn, channel);
1441 return SILC_FSM_FINISH;
1444 /********************************** MOTD ************************************/
1446 /* MOTD command. Requests motd from server. */
1448 SILC_FSM_STATE(silc_client_command_motd)
1450 SilcClientCommandContext cmd = fsm_context;
1451 SilcClientConnection conn = cmd->conn;
1453 if (cmd->argc < 1 || cmd->argc > 2) {
1454 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1455 "Usage: /MOTD [<server>]");
1456 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1457 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1458 return SILC_FSM_FINISH;
1461 /* Send the command */
1463 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1464 1, conn->remote_host,
1465 strlen(conn->remote_host));
1467 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1468 1, cmd->argv[1], cmd->argv_lens[1]);
1470 /* Notify application */
1471 COMMAND(SILC_STATUS_OK);
1473 /** Wait for command reply */
1474 silc_fsm_next(fsm, silc_client_command_reply_wait);
1475 return SILC_FSM_CONTINUE;
1478 /********************************** UMODE ***********************************/
1480 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1481 modes as client cannot set itself server/router operator privileges. */
1483 SILC_FSM_STATE(silc_client_command_umode)
1485 SilcClientCommandContext cmd = fsm_context;
1486 SilcClientConnection conn = cmd->conn;
1487 unsigned char *cp, modebuf[4];
1488 SilcUInt32 mode, add, len;
1491 if (cmd->argc < 2) {
1492 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1493 "Usage: /UMODE +|-<modes>");
1494 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1495 return SILC_FSM_FINISH;
1498 mode = conn->local_entry->mode;
1500 /* Are we adding or removing mode */
1501 if (cmd->argv[1][0] == '-')
1507 cp = cmd->argv[1] + 1;
1509 for (i = 0; i < len; i++) {
1514 mode |= SILC_UMODE_SERVER_OPERATOR;
1515 mode |= SILC_UMODE_ROUTER_OPERATOR;
1516 mode |= SILC_UMODE_GONE;
1517 mode |= SILC_UMODE_INDISPOSED;
1518 mode |= SILC_UMODE_BUSY;
1519 mode |= SILC_UMODE_PAGE;
1520 mode |= SILC_UMODE_HYPER;
1521 mode |= SILC_UMODE_ROBOT;
1522 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1523 mode |= SILC_UMODE_REJECT_WATCHING;
1525 mode = SILC_UMODE_NONE;
1530 mode |= SILC_UMODE_SERVER_OPERATOR;
1532 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1536 mode |= SILC_UMODE_ROUTER_OPERATOR;
1538 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1542 mode |= SILC_UMODE_GONE;
1544 mode &= ~SILC_UMODE_GONE;
1548 mode |= SILC_UMODE_INDISPOSED;
1550 mode &= ~SILC_UMODE_INDISPOSED;
1554 mode |= SILC_UMODE_BUSY;
1556 mode &= ~SILC_UMODE_BUSY;
1560 mode |= SILC_UMODE_PAGE;
1562 mode &= ~SILC_UMODE_PAGE;
1566 mode |= SILC_UMODE_HYPER;
1568 mode &= ~SILC_UMODE_HYPER;
1572 mode |= SILC_UMODE_ROBOT;
1574 mode &= ~SILC_UMODE_ROBOT;
1578 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1580 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1584 mode |= SILC_UMODE_REJECT_WATCHING;
1586 mode &= ~SILC_UMODE_REJECT_WATCHING;
1590 mode |= SILC_UMODE_BLOCK_INVITE;
1592 mode &= ~SILC_UMODE_BLOCK_INVITE;
1595 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1596 return SILC_FSM_FINISH;
1601 SILC_PUT32_MSB(mode, modebuf);
1603 /* Send the command */
1604 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1605 1, silc_buffer_datalen(conn->internal->
1607 2, modebuf, sizeof(modebuf));
1609 /* Notify application */
1610 COMMAND(SILC_STATUS_OK);
1612 /** Wait for command reply */
1613 silc_fsm_next(fsm, silc_client_command_reply_wait);
1614 return SILC_FSM_CONTINUE;
1617 /********************************** CMODE ***********************************/
1619 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1620 can be set several at once. Those modes that require argument must be set
1621 separately (unless set with modes that does not require arguments). */
1623 SILC_FSM_STATE(silc_client_command_cmode)
1625 SilcClientCommandContext cmd = fsm_context;
1626 SilcClientConnection conn = cmd->conn;
1627 SilcClient client = conn->client;
1628 SilcChannelEntry channel = NULL;
1629 SilcBuffer chidp, auth = NULL, pk = NULL;
1630 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1631 SilcUInt32 mode, add, type, len, arg_len = 0;
1634 if (cmd->argc < 3) {
1635 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1636 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1637 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1641 if (cmd->argv[1][0] == '*') {
1642 if (!conn->current_channel) {
1643 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1647 channel = conn->current_channel;
1648 silc_client_ref_channel(client, conn, channel);
1650 name = cmd->argv[1];
1652 channel = silc_client_get_channel(conn->client, conn, name);
1654 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1659 mode = channel->mode;
1661 /* Are we adding or removing mode */
1662 if (cmd->argv[2][0] == '-')
1667 /* Argument type to be sent to server */
1671 cp = cmd->argv[2] + 1;
1673 for (i = 0; i < len; i++) {
1677 mode |= SILC_CHANNEL_MODE_PRIVATE;
1679 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1683 mode |= SILC_CHANNEL_MODE_SECRET;
1685 mode &= ~SILC_CHANNEL_MODE_SECRET;
1689 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1691 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1695 mode |= SILC_CHANNEL_MODE_INVITE;
1697 mode &= ~SILC_CHANNEL_MODE_INVITE;
1701 mode |= SILC_CHANNEL_MODE_TOPIC;
1703 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1707 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1709 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1713 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1715 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1720 mode |= SILC_CHANNEL_MODE_ULIMIT;
1722 if (cmd->argc < 4) {
1723 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1724 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1725 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1728 ll = atoi(cmd->argv[3]);
1729 SILC_PUT32_MSB(ll, tmp);
1733 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1738 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1740 if (cmd->argc < 4) {
1741 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1742 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1743 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1747 arg_len = cmd->argv_lens[3];
1749 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1754 mode |= SILC_CHANNEL_MODE_CIPHER;
1756 if (cmd->argc < 4) {
1757 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1758 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1759 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1763 arg_len = cmd->argv_lens[3];
1765 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1770 mode |= SILC_CHANNEL_MODE_HMAC;
1772 if (cmd->argc < 4) {
1773 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1774 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1775 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1779 arg_len = cmd->argv_lens[3];
1781 mode &= ~SILC_CHANNEL_MODE_HMAC;
1786 SilcPublicKey pubkey = conn->public_key;
1787 SilcPrivateKey privkey = conn->private_key;
1789 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1792 if (cmd->argc >= 5) {
1795 pass = cmd->argv[5];
1796 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1797 &pubkey, &privkey)) {
1798 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1799 "Could not load key pair, check your arguments");
1800 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1805 pk = silc_public_key_payload_encode(pubkey);
1806 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1808 conn->internal->sha1hash,
1811 arg = silc_buffer_data(auth);
1812 arg_len = silc_buffer_len(auth);
1814 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1820 SilcBool chadd = FALSE;
1821 SilcPublicKey chpk = NULL;
1823 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1826 if (cmd->argc == 3) {
1827 /* Send empty command to receive the public key list. */
1828 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1829 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1831 1, silc_buffer_datalen(chidp));
1832 silc_buffer_free(chidp);
1834 /* Notify application */
1835 COMMAND(SILC_STATUS_OK);
1839 if (cmd->argc >= 4) {
1840 auth = silc_buffer_alloc_size(2);
1841 silc_buffer_format(auth,
1842 SILC_STR_UI_SHORT(cmd->argc - 3),
1846 for (k = 3; k < cmd->argc; k++) {
1847 if (cmd->argv[k][0] == '+')
1849 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1850 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1851 "Could not load public key %s, check the filename",
1853 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1854 silc_buffer_free(auth);
1859 pk = silc_public_key_payload_encode(chpk);
1860 auth = silc_argument_payload_encode_one(auth,
1861 silc_buffer_datalen(pk),
1862 chadd ? 0x00 : 0x01);
1863 silc_pkcs_public_key_free(chpk);
1864 silc_buffer_free(pk);
1869 arg = silc_buffer_data(auth);
1870 arg_len = silc_buffer_len(auth);
1872 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1876 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1882 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1883 SILC_PUT32_MSB(mode, modebuf);
1885 /* Send the command. We support sending only one mode at once that
1886 requires an argument. */
1888 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1889 1, silc_buffer_datalen(chidp),
1890 2, modebuf, sizeof(modebuf),
1892 8, silc_buffer_datalen(pk));
1894 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1895 1, silc_buffer_datalen(chidp),
1896 2, modebuf, sizeof(modebuf));
1899 silc_buffer_free(chidp);
1900 silc_buffer_free(auth);
1901 silc_buffer_free(pk);
1902 silc_client_unref_channel(client, conn, channel);
1904 /* Notify application */
1905 COMMAND(SILC_STATUS_OK);
1907 /** Wait for command reply */
1908 silc_fsm_next(fsm, silc_client_command_reply_wait);
1909 return SILC_FSM_CONTINUE;
1912 silc_client_unref_channel(client, conn, channel);
1913 return SILC_FSM_FINISH;
1916 /********************************* CUMODE ***********************************/
1918 /* CUMODE command. Changes client's mode on a channel. */
1920 SILC_FSM_STATE(silc_client_command_cumode)
1922 SilcClientCommandContext cmd = fsm_context;
1923 SilcClientConnection conn = cmd->conn;
1924 SilcClient client = conn->client;
1925 SilcChannelEntry channel = NULL;
1926 SilcChannelUser chu;
1927 SilcClientEntry client_entry;
1928 SilcBuffer clidp, chidp, auth = NULL;
1929 SilcDList clients = NULL;
1930 unsigned char *name, *cp, modebuf[4];
1931 SilcUInt32 mode = 0, add, len;
1932 char *nickname = NULL;
1935 if (cmd->argc < 4) {
1936 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1937 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1938 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1942 if (cmd->argv[1][0] == '*') {
1943 if (!conn->current_channel) {
1944 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1948 channel = conn->current_channel;
1949 silc_client_ref_channel(client, conn, channel);
1951 name = cmd->argv[1];
1953 channel = silc_client_get_channel(conn->client, conn, name);
1955 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1960 /* Parse the typed nickname. */
1961 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1963 /* Find client entry */
1964 clients = silc_client_get_clients_local(client, conn, nickname,
1967 /* Resolve client information */
1968 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1969 silc_client_command_resolve_continue,
1972 client_entry = silc_dlist_get(clients);
1974 /* Get the current mode */
1975 chu = silc_client_on_channel(channel, client_entry);
1979 /* Are we adding or removing mode */
1980 if (cmd->argv[2][0] == '-')
1986 cp = cmd->argv[2] + 1;
1988 for (i = 0; i < len; i++) {
1992 mode |= SILC_CHANNEL_UMODE_CHANFO;
1993 mode |= SILC_CHANNEL_UMODE_CHANOP;
1994 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1995 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1996 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1998 mode = SILC_CHANNEL_UMODE_NONE;
2003 SilcPublicKey pubkey = conn->public_key;
2004 SilcPrivateKey privkey = conn->private_key;
2006 if (cmd->argc >= 6) {
2009 pass = cmd->argv[6];
2010 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2011 &pubkey, &privkey)) {
2012 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2013 "Could not load key pair, check your arguments");
2014 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2019 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
2021 conn->internal->sha1hash,
2024 mode |= SILC_CHANNEL_UMODE_CHANFO;
2026 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2031 mode |= SILC_CHANNEL_UMODE_CHANOP;
2033 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2037 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2039 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2043 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2045 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2049 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2051 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2055 mode |= SILC_CHANNEL_UMODE_QUIET;
2057 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2060 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2066 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2067 SILC_PUT32_MSB(mode, modebuf);
2068 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2070 /* Send the command packet. We support sending only one mode at once
2071 that requires an argument. */
2072 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2073 1, silc_buffer_datalen(chidp),
2075 3, silc_buffer_datalen(clidp),
2076 4, silc_buffer_datalen(auth));
2078 silc_buffer_free(chidp);
2079 silc_buffer_free(clidp);
2081 silc_buffer_free(auth);
2082 silc_free(nickname);
2083 silc_client_list_free(client, conn, clients);
2084 silc_client_unref_channel(client, conn, channel);
2086 /* Notify application */
2087 COMMAND(SILC_STATUS_OK);
2089 /** Wait for command reply */
2090 silc_fsm_next(fsm, silc_client_command_reply_wait);
2091 return SILC_FSM_CONTINUE;
2094 silc_client_unref_channel(client, conn, channel);
2095 silc_client_list_free(client, conn, clients);
2096 silc_free(nickname);
2097 return SILC_FSM_FINISH;
2100 /********************************** KICK ************************************/
2102 /* KICK command. Kicks a client out of channel. */
2104 SILC_FSM_STATE(silc_client_command_kick)
2106 SilcClientCommandContext cmd = fsm_context;
2107 SilcClientConnection conn = cmd->conn;
2108 SilcClient client = conn->client;
2109 SilcChannelEntry channel = NULL;
2110 SilcBuffer idp, idp2;
2111 SilcClientEntry target;
2112 SilcDList clients = NULL;
2114 char *nickname = NULL;
2116 if (cmd->argc < 3) {
2117 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2118 "Usage: /KICK <channel> <nickname> [<comment>]");
2119 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2123 if (cmd->argv[1][0] == '*') {
2124 if (!conn->current_channel) {
2125 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2128 name = conn->current_channel->channel_name;
2130 name = cmd->argv[1];
2133 if (!conn->current_channel) {
2134 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2138 /* Get the Channel ID of the channel */
2139 channel = silc_client_get_channel(conn->client, conn, name);
2141 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2145 /* Parse the typed nickname. */
2146 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
2148 /* Get the target client */
2149 clients = silc_client_get_clients_local(client, conn, nickname,
2152 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2153 "No such client: %s", cmd->argv[2]);
2154 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2157 target = silc_dlist_get(clients);
2159 /* Send KICK command to the server */
2160 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2161 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2163 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2164 1, silc_buffer_datalen(idp),
2165 2, silc_buffer_datalen(idp2));
2167 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2168 1, silc_buffer_datalen(idp),
2169 2, silc_buffer_datalen(idp2),
2170 3, cmd->argv[3], strlen(cmd->argv[3]));
2172 silc_buffer_free(idp);
2173 silc_buffer_free(idp2);
2174 silc_free(nickname);
2175 silc_client_list_free(client, conn, clients);
2176 silc_client_unref_channel(client, conn, channel);
2178 /* Notify application */
2179 COMMAND(SILC_STATUS_OK);
2181 /** Wait for command reply */
2182 silc_fsm_next(fsm, silc_client_command_reply_wait);
2183 return SILC_FSM_CONTINUE;
2186 silc_client_unref_channel(client, conn, channel);
2187 silc_free(nickname);
2188 return SILC_FSM_FINISH;
2191 /***************************** OPER & SILCOPER ******************************/
2194 unsigned char *passphrase;
2195 SilcUInt32 passphrase_len;
2196 } *SilcClientCommandOper;
2198 /* Ask passphrase callback */
2200 static void silc_client_command_oper_cb(unsigned char *data,
2201 SilcUInt32 data_len, void *context)
2203 SilcClientCommandContext cmd = context;
2204 SilcClientCommandOper oper = cmd->context;
2206 if (data && data_len)
2207 oper->passphrase = silc_memdup(data, data_len);
2208 oper->passphrase_len = data_len;
2211 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2214 /* Send OPER/SILCOPER command */
2216 SILC_FSM_STATE(silc_client_command_oper_send)
2218 SilcClientCommandContext cmd = fsm_context;
2219 SilcClientConnection conn = cmd->conn;
2220 SilcClientCommandOper oper = cmd->context;
2223 if (!oper || !oper->passphrase) {
2224 /* Encode the public key authentication payload */
2225 auth = silc_auth_public_key_auth_generate(conn->public_key,
2228 conn->internal->hash,
2232 /* Encode the password authentication payload */
2233 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2234 oper->passphrase, oper->passphrase_len);
2237 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2238 1, cmd->argv[1], strlen(cmd->argv[1]),
2239 2, silc_buffer_datalen(auth));
2241 silc_buffer_clear(auth);
2242 silc_buffer_free(auth);
2244 silc_free(oper->passphrase);
2248 /* Notify application */
2249 COMMAND(SILC_STATUS_OK);
2251 /** Wait for command reply */
2252 silc_fsm_next(fsm, silc_client_command_reply_wait);
2253 return SILC_FSM_CONTINUE;
2256 /* OPER command. Used to obtain server operator privileges. */
2258 SILC_FSM_STATE(silc_client_command_oper)
2260 SilcClientCommandContext cmd = fsm_context;
2261 SilcClientConnection conn = cmd->conn;
2262 SilcClientCommandOper oper;
2264 if (cmd->argc < 2) {
2265 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2266 "Usage: /OPER <username> [-pubkey]");
2267 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2268 return SILC_FSM_FINISH;
2271 /* Get passphrase */
2272 if (cmd->argc < 3) {
2273 oper = silc_calloc(1, sizeof(*oper));
2275 return SILC_FSM_FINISH;
2276 cmd->context = oper;
2277 SILC_FSM_CALL(conn->client->internal->
2278 ops->ask_passphrase(conn->client, conn,
2279 silc_client_command_oper_cb, cmd));
2282 silc_fsm_next(fsm, silc_client_command_oper_send);
2283 return SILC_FSM_CONTINUE;
2286 /* SILCOPER command. Used to obtain router operator privileges. */
2288 SILC_FSM_STATE(silc_client_command_silcoper)
2290 SilcClientCommandContext cmd = fsm_context;
2291 SilcClientConnection conn = cmd->conn;
2292 SilcClientCommandOper oper;
2294 if (cmd->argc < 2) {
2295 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2296 "Usage: /SILCOPER <username> [-pubkey]");
2297 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2298 return SILC_FSM_FINISH;
2301 /* Get passphrase */
2302 if (cmd->argc < 3) {
2303 oper = silc_calloc(1, sizeof(*oper));
2305 return SILC_FSM_FINISH;
2306 cmd->context = oper;
2307 SILC_FSM_CALL(conn->client->internal->
2308 ops->ask_passphrase(conn->client, conn,
2309 silc_client_command_oper_cb, cmd));
2312 silc_fsm_next(fsm, silc_client_command_oper_send);
2313 return SILC_FSM_CONTINUE;
2316 /*********************************** BAN ************************************/
2318 /* Command BAN. This is used to manage the ban list of the channel. */
2320 SILC_FSM_STATE(silc_client_command_ban)
2322 SilcClientCommandContext cmd = fsm_context;
2323 SilcClientConnection conn = cmd->conn;
2324 SilcClient client = conn->client;
2325 SilcChannelEntry channel;
2326 SilcBuffer chidp, args = NULL;
2327 char *name, *ban = NULL;
2328 unsigned char action[1];
2329 SilcPublicKey pubkey = NULL;
2331 if (cmd->argc < 2) {
2332 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2333 "Usage: /BAN <channel> "
2334 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2335 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2339 if (cmd->argv[1][0] == '*') {
2340 if (!conn->current_channel) {
2341 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2345 channel = conn->current_channel;
2346 silc_client_ref_channel(client, conn, channel);
2348 name = cmd->argv[1];
2350 channel = silc_client_get_channel(conn->client, conn, name);
2352 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2357 if (cmd->argc == 3) {
2358 if (cmd->argv[2][0] == '+')
2363 /* Check if it is public key file to be added to invite list */
2364 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2371 args = silc_buffer_alloc_size(2);
2372 silc_buffer_format(args,
2373 SILC_STR_UI_SHORT(1),
2376 chidp = silc_public_key_payload_encode(pubkey);
2377 args = silc_argument_payload_encode_one(args,
2378 silc_buffer_datalen(chidp), 2);
2379 silc_buffer_free(chidp);
2380 silc_pkcs_public_key_free(pubkey);
2382 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2386 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2388 /* Send the command */
2389 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2390 1, silc_buffer_datalen(chidp),
2391 2, args ? action : NULL, args ? 1 : 0,
2392 3, silc_buffer_datalen(args));
2394 silc_buffer_free(chidp);
2395 silc_buffer_free(args);
2396 silc_client_unref_channel(client, conn, channel);
2398 /* Notify application */
2399 COMMAND(SILC_STATUS_OK);
2401 /** Wait for command reply */
2402 silc_fsm_next(fsm, silc_client_command_reply_wait);
2403 return SILC_FSM_CONTINUE;
2406 return SILC_FSM_FINISH;
2409 /********************************* DETACH ***********************************/
2411 /* Command DETACH. This is used to detach from the server */
2413 SILC_FSM_STATE(silc_client_command_detach)
2415 SilcClientCommandContext cmd = fsm_context;
2416 SilcClientConnection conn = cmd->conn;
2418 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2420 /* Notify application */
2421 COMMAND(SILC_STATUS_OK);
2423 /** Wait for command reply */
2424 silc_fsm_next(fsm, silc_client_command_reply_wait);
2425 return SILC_FSM_CONTINUE;
2428 /********************************** WATCH ***********************************/
2430 /* Command WATCH. */
2432 SILC_FSM_STATE(silc_client_command_watch)
2434 SilcClientCommandContext cmd = fsm_context;
2435 SilcClientConnection conn = cmd->conn;
2436 SilcBuffer args = NULL;
2438 const char *pubkey = NULL;
2439 SilcBool pubkey_add = TRUE;
2441 if (cmd->argc < 3) {
2442 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2446 if (!strcasecmp(cmd->argv[1], "-add")) {
2448 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2450 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2452 pubkey = cmd->argv[2] + 1;
2453 if (cmd->argv[2][0] == '-')
2456 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2464 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2465 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2466 "Could not load public key %s, check the filename", pubkey);
2467 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2471 args = silc_buffer_alloc_size(2);
2472 silc_buffer_format(args,
2473 SILC_STR_UI_SHORT(1),
2475 buffer = silc_public_key_payload_encode(pk);
2476 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2477 pubkey_add ? 0x00 : 0x01);
2478 silc_buffer_free(buffer);
2479 silc_pkcs_public_key_free(pk);
2482 /* If watching by nickname, resolve all users with that nickname so that
2483 we get their information immediately. */
2485 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2486 silc_client_command_resolve_dummy, NULL);
2488 /* Send the commmand */
2489 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2490 1, silc_buffer_datalen(conn->internal->
2492 type, pubkey ? args->data : cmd->argv[2],
2493 pubkey ? silc_buffer_len(args) :
2496 silc_buffer_free(args);
2498 /* Notify application */
2499 COMMAND(SILC_STATUS_OK);
2501 /** Wait for command reply */
2502 silc_fsm_next(fsm, silc_client_command_reply_wait);
2503 return SILC_FSM_CONTINUE;
2506 return SILC_FSM_FINISH;
2509 /********************************** LEAVE ***********************************/
2511 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2513 SILC_FSM_STATE(silc_client_command_leave)
2515 SilcClientCommandContext cmd = fsm_context;
2516 SilcClientConnection conn = cmd->conn;
2517 SilcClient client = conn->client;
2518 SilcChannelEntry channel;
2522 if (cmd->argc != 2) {
2523 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2524 "Usage: /LEAVE <channel>");
2525 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2529 if (cmd->argv[1][0] == '*') {
2530 if (!conn->current_channel) {
2531 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2534 name = conn->current_channel->channel_name;
2536 name = cmd->argv[1];
2539 /* Get the channel entry */
2540 channel = silc_client_get_channel(conn->client, conn, name);
2542 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2546 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2548 /* Send LEAVE command to the server */
2549 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2550 1, silc_buffer_datalen(idp));
2552 silc_buffer_free(idp);
2554 /* Notify application */
2555 COMMAND(SILC_STATUS_OK);
2557 if (conn->current_channel == channel)
2558 conn->current_channel = NULL;
2560 silc_client_unref_channel(client, conn, channel);
2562 /** Wait for command reply */
2563 silc_fsm_next(fsm, silc_client_command_reply_wait);
2564 return SILC_FSM_CONTINUE;
2567 return SILC_FSM_FINISH;
2570 /********************************** USERS ***********************************/
2572 /* Command USERS. Requests the USERS of the clients joined on requested
2575 SILC_FSM_STATE(silc_client_command_users)
2577 SilcClientCommandContext cmd = fsm_context;
2578 SilcClientConnection conn = cmd->conn;
2581 if (cmd->argc != 2) {
2582 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2583 "Usage: /USERS <channel>");
2584 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2588 if (cmd->argv[1][0] == '*') {
2589 if (!conn->current_channel) {
2590 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2593 name = conn->current_channel->channel_name;
2595 name = cmd->argv[1];
2598 /* Send USERS command to the server */
2599 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2600 2, name, strlen(name));
2602 /* Notify application */
2603 COMMAND(SILC_STATUS_OK);
2605 /** Wait for command reply */
2606 silc_fsm_next(fsm, silc_client_command_reply_wait);
2607 return SILC_FSM_CONTINUE;
2610 return SILC_FSM_FINISH;
2613 /********************************* GETKEY ***********************************/
2615 /* Command GETKEY. Used to fetch remote client's public key. */
2617 SILC_FSM_STATE(silc_client_command_getkey)
2619 SilcClientCommandContext cmd = fsm_context;
2620 SilcClientConnection conn = cmd->conn;
2621 SilcClient client = conn->client;
2622 SilcClientEntry client_entry;
2623 SilcServerEntry server_entry;
2625 char *nickname = NULL;
2628 if (cmd->argc < 2) {
2629 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2630 "Usage: /GETKEY <nickname or server name>");
2631 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2632 return SILC_FSM_FINISH;
2635 /* Parse the typed nickname. */
2636 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname)) {
2637 COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2638 return SILC_FSM_FINISH;
2641 /* Find client entry */
2642 clients = silc_client_get_clients_local(client, conn, nickname,
2645 /* Check whether user requested server */
2646 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2647 if (!server_entry) {
2648 if (cmd->resolved) {
2649 /* Resolving didn't find anything. We should never get here as
2650 errors are handled in the resolving callback. */
2651 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2652 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2653 return SILC_FSM_FINISH;
2656 /* No client or server exist with this name, query for both. */
2657 cmd->resolved = TRUE;
2658 SILC_FSM_CALL(silc_client_command_send(client, conn,
2659 SILC_COMMAND_IDENTIFY,
2660 silc_client_command_continue,
2663 strlen(cmd->argv[1]),
2665 strlen(cmd->argv[1])));
2668 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2669 silc_client_unref_server(client, conn, server_entry);
2671 client_entry = silc_dlist_get(clients);
2672 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2673 silc_client_list_free(client, conn, clients);
2676 /* Send the commmand */
2677 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2678 1, silc_buffer_datalen(idp));
2680 silc_buffer_free(idp);
2681 silc_free(nickname);
2683 /* Notify application */
2684 COMMAND(SILC_STATUS_OK);
2686 /** Wait for command reply */
2687 silc_fsm_next(fsm, silc_client_command_reply_wait);
2688 return SILC_FSM_CONTINUE;
2691 /********************************* SERVICE **********************************/
2693 /* Command SERVICE. Negotiates service agreement with server. */
2694 /* XXX incomplete */
2696 SILC_FSM_STATE(silc_client_command_service)
2698 SilcClientCommandContext cmd = fsm_context;
2700 SilcClientConnection conn = cmd->conn;
2704 if (cmd->argc < 2) {
2705 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2706 "Usage: /SERVICE [<service name>] [-pubkey]");
2707 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2708 return SILC_FSM_FINISH;
2711 name = cmd->argv[1];
2713 /* Send SERVICE command to the server */
2714 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2715 ++conn->cmd_ident, 1,
2716 1, name, strlen(name));
2717 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2718 NULL, 0, NULL, NULL, buffer->data,
2720 silc_buffer_free(buffer);
2723 /* Notify application */
2724 COMMAND(SILC_STATUS_OK);
2726 /** Wait for command reply */
2727 silc_fsm_next(fsm, silc_client_command_reply_wait);
2728 return SILC_FSM_CONTINUE;
2731 /* Register all default commands provided by the client library for the
2734 void silc_client_commands_register(SilcClient client)
2736 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2739 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2740 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2741 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2742 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2743 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2744 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2745 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2746 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2747 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2748 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2749 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2750 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2751 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2752 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2753 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2754 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2755 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2756 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2757 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2758 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2759 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2760 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2761 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2762 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2763 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2764 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2765 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2768 /* Unregister all commands. */
2770 void silc_client_commands_unregister(SilcClient client)
2772 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2773 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2774 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2775 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2776 SILC_CLIENT_CMDU(list, LIST, "LIST");
2777 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2778 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2779 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2780 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2781 SILC_CLIENT_CMDU(info, INFO, "INFO");
2782 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2783 SILC_CLIENT_CMDU(ping, PING, "PING");
2784 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2785 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2786 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2787 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2788 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2789 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2790 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2791 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2792 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2793 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2794 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2795 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2796 SILC_CLIENT_CMDU(users, USERS, "USERS");
2797 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2798 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2801 /****************** Client Side Incoming Command Handling *******************/
2803 /* Reply to WHOIS command from server */
2805 static void silc_client_command_process_whois(SilcClient client,
2806 SilcClientConnection conn,
2807 SilcCommandPayload payload,
2808 SilcArgumentPayload args)
2813 SilcBuffer buffer, packet;
2815 SILC_LOG_DEBUG(("Received WHOIS command"));
2817 /* Try to take the Requested Attributes */
2818 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2822 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2826 /* Process requested attributes */
2827 buffer = silc_client_attributes_process(client, conn, attrs);
2829 silc_attribute_payload_list_free(attrs);
2833 /* Send the attributes back in COMMAND_REPLY packet */
2835 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2837 silc_command_get_ident(payload),
2838 1, 11, buffer->data,
2839 silc_buffer_len(buffer));
2841 silc_buffer_free(buffer);
2845 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
2847 silc_packet_send(conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
2848 silc_buffer_datalen(packet));
2850 silc_buffer_free(packet);
2851 silc_buffer_free(buffer);
2854 /* Client is able to receive some command packets even though they are
2855 special case. Server may send WHOIS command to the client to retrieve
2856 Requested Attributes information for WHOIS query the server is
2857 processing. This function currently handles only the WHOIS command,
2858 but if in the future more commands may arrive then this can be made
2859 to support other commands too. */
2861 SILC_FSM_STATE(silc_client_command)
2863 SilcClientConnection conn = fsm_context;
2864 SilcClient client = conn->client;
2865 SilcPacket packet = state_context;
2866 SilcCommandPayload payload;
2867 SilcCommand command;
2868 SilcArgumentPayload args;
2870 /* Get command payload from packet */
2871 payload = silc_command_payload_parse(packet->buffer.data,
2872 silc_buffer_len(&packet->buffer));
2874 SILC_LOG_DEBUG(("Bad command packet"));
2875 return SILC_FSM_FINISH;
2879 args = silc_command_get_args(payload);
2881 /* Get the command */
2882 command = silc_command_get(payload);
2885 case SILC_COMMAND_WHOIS:
2886 /* Ignore everything if requested by application */
2887 if (conn->internal->params.ignore_requested_attributes)
2890 silc_client_command_process_whois(client, conn, payload, args);
2897 silc_command_payload_free(payload);
2898 return SILC_FSM_FINISH;