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;
639 char *nickname = NULL;
642 /* Given without arguments fetches client's own information */
644 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
645 silc_buffer_data(conn->internal->local_idp),
646 silc_buffer_len(conn->internal->local_idp));
648 /* Notify application */
649 COMMAND(SILC_STATUS_OK);
651 /** Wait for command reply */
652 silc_fsm_next(fsm, silc_client_command_reply_wait);
653 return SILC_FSM_CONTINUE;
656 for (i = 1; i < cmd->argc; i++) {
657 if (!strcasecmp(cmd->argv[i], "-details")) {
659 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
660 pubkey = cmd->argv[i + 1];
663 /* We assume that the first parameter is the nickname, if it isn't
664 -details or -pubkey. The last parameter should always be the count */
667 } else if (i == cmd->argc - 1) {
668 int c = atoi(cmd->argv[i]);
669 SILC_PUT32_MSB(c, count);
676 /* If pubkey is set, add all attributes to the attrs buffer, except
679 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
680 SILC_ATTRIBUTE_SERVICE,
681 SILC_ATTRIBUTE_STATUS_MOOD,
682 SILC_ATTRIBUTE_STATUS_FREETEXT,
683 SILC_ATTRIBUTE_STATUS_MESSAGE,
684 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
685 SILC_ATTRIBUTE_PREFERRED_CONTACT,
686 SILC_ATTRIBUTE_TIMEZONE,
687 SILC_ATTRIBUTE_GEOLOCATION,
688 SILC_ATTRIBUTE_DEVICE_INFO,
689 SILC_ATTRIBUTE_USER_ICON, 0);
691 attrs = silc_client_attributes_request(0);
696 SilcAttributeObjPk obj;
699 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
700 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
701 "Could not load public key %s, check the filename",
703 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
707 switch (silc_pkcs_get_type(pk)) {
709 obj.type = "silc-rsa";
712 obj.type = "ssh-rsa";
714 case SILC_PKCS_X509V3:
715 obj.type = "x509v3-sign-rsa";
717 case SILC_PKCS_OPENPGP:
718 obj.type = "pgp-sign-rsa";
724 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
726 attrs = silc_attribute_payload_encode(attrs,
727 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
728 SILC_ATTRIBUTE_FLAG_VALID,
734 silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname);
736 nickname = strdup(cmd->argv[1]);
740 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
741 3, 1, nick ? nickname : NULL,
742 nick ? strlen(nickname) : 0,
743 2, tmp ? tmp : NULL, tmp ? 4 : 0,
744 3, silc_buffer_datalen(attrs));
747 /* Notify application */
748 COMMAND(SILC_STATUS_OK);
750 /** Wait for command reply */
751 silc_fsm_next(fsm, silc_client_command_reply_wait);
752 return SILC_FSM_CONTINUE;
755 return SILC_FSM_FINISH;
758 /******************************** WHOWAS ************************************/
760 /* Command WHOWAS. This command is used to query history information about
761 specific user that used to exist in the network. */
763 SILC_FSM_STATE(silc_client_command_whowas)
765 SilcClientCommandContext cmd = fsm_context;
766 SilcClientConnection conn = cmd->conn;
767 unsigned char count[4];
770 if (cmd->argc < 2 || cmd->argc > 3) {
771 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
772 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
773 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
774 SILC_STATUS_ERR_TOO_MANY_PARAMS));
775 return SILC_FSM_FINISH;
778 if (cmd->argc == 2) {
779 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
780 1, 1, cmd->argv[1], cmd->argv_lens[1]);
782 c = atoi(cmd->argv[2]);
783 SILC_PUT32_MSB(c, count);
784 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
785 2, 1, cmd->argv[1], cmd->argv_lens[1],
786 2, count, sizeof(count));
789 /* Notify application */
790 COMMAND(SILC_STATUS_OK);
792 /** Wait for command reply */
793 silc_fsm_next(fsm, silc_client_command_reply_wait);
794 return SILC_FSM_CONTINUE;
797 /******************************** IDENTIFY **********************************/
799 /* Command IDENTIFY. This command is used to query information about
800 specific user, especially ID's. */
802 SILC_FSM_STATE(silc_client_command_identify)
804 SilcClientCommandContext cmd = fsm_context;
805 SilcClientConnection conn = cmd->conn;
806 unsigned char count[4];
809 if (cmd->argc < 2 || cmd->argc > 3)
810 return SILC_FSM_FINISH;
812 if (cmd->argc == 2) {
813 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
814 1, 1, cmd->argv[1], cmd->argv_lens[1]);
816 c = atoi(cmd->argv[2]);
817 SILC_PUT32_MSB(c, count);
818 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
819 2, 1, cmd->argv[1], cmd->argv_lens[1],
820 4, count, sizeof(count));
823 /** Wait for command reply */
824 silc_fsm_next(fsm, silc_client_command_reply_wait);
825 return SILC_FSM_CONTINUE;
828 /********************************** NICK ************************************/
830 /* Command NICK. Shows current nickname/sets new nickname on current
833 SILC_FSM_STATE(silc_client_command_nick)
835 SilcClientCommandContext cmd2, cmd = fsm_context;
836 SilcClientConnection conn = cmd->conn;
839 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
840 "Usage: /NICK <nickname>");
841 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
845 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
848 /* Show current nickname */
851 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
852 "Your nickname is %s on server %s",
853 conn->local_entry->nickname, conn->remote_host);
855 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
856 "Your nickname is %s", conn->local_entry->nickname);
859 COMMAND(SILC_STATUS_OK);
863 /* If JOIN command is active, wait for it to finish before sending NICK.
864 To avoid problems locally with changing IDs while joining, we do this. */
865 silc_mutex_lock(conn->internal->lock);
866 silc_list_start(conn->internal->pending_commands);
867 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
868 if (cmd2->cmd == SILC_COMMAND_JOIN) {
869 silc_mutex_unlock(conn->internal->lock);
870 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
871 return SILC_FSM_WAIT;
874 silc_mutex_unlock(conn->internal->lock);
876 if (cmd->argv_lens[1] > 128)
877 cmd->argv_lens[1] = 128;
879 /* Send the NICK command */
880 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
881 1, 1, cmd->argv[1], cmd->argv_lens[1]);
883 /* Notify application */
884 COMMAND(SILC_STATUS_OK);
886 /** Wait for command reply */
887 silc_fsm_next(fsm, silc_client_command_reply_wait);
888 return SILC_FSM_CONTINUE;
891 return SILC_FSM_FINISH;
894 /********************************** LIST ************************************/
896 /* Command LIST. Lists channels on the current server. */
898 SILC_FSM_STATE(silc_client_command_list)
900 SilcClientCommandContext cmd = fsm_context;
901 SilcClientConnection conn = cmd->conn;
902 SilcClient client = conn->client;
903 SilcChannelEntry channel = NULL;
904 SilcBuffer idp = NULL;
906 if (cmd->argc == 2) {
907 /* Get the Channel ID of the channel */
908 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
910 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
914 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
916 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
917 1, 1, silc_buffer_datalen(idp));
919 silc_buffer_free(idp);
920 silc_client_unref_channel(client, conn, channel);
922 /* Notify application */
923 COMMAND(SILC_STATUS_OK);
925 /** Wait for command reply */
926 silc_fsm_next(fsm, silc_client_command_reply_wait);
927 return SILC_FSM_CONTINUE;
930 /********************************** TOPIC ***********************************/
932 /* Command TOPIC. Sets/shows topic on a channel. */
934 SILC_FSM_STATE(silc_client_command_topic)
936 SilcClientCommandContext cmd = fsm_context;
937 SilcClientConnection conn = cmd->conn;
938 SilcClient client = conn->client;
939 SilcChannelEntry channel;
943 if (cmd->argc < 2 || cmd->argc > 3) {
944 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
945 "Usage: /TOPIC <channel> [<topic>]");
946 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
947 SILC_STATUS_ERR_TOO_MANY_PARAMS));
951 if (cmd->argv[1][0] == '*') {
952 if (!conn->current_channel) {
953 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
956 name = conn->current_channel->channel_name;
961 if (!conn->current_channel) {
962 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
966 /* Get the Channel ID of the channel */
967 channel = silc_client_get_channel(conn->client, conn, name);
969 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
973 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
975 /* Send TOPIC command to the server */
977 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
978 1, silc_buffer_datalen(idp),
979 2, cmd->argv[2], strlen(cmd->argv[2]));
981 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
982 1, silc_buffer_datalen(idp));
984 silc_buffer_free(idp);
985 silc_client_unref_channel(client, conn, channel);
987 /* Notify application */
988 COMMAND(SILC_STATUS_OK);
990 /** Wait for command reply */
991 silc_fsm_next(fsm, silc_client_command_reply_wait);
992 return SILC_FSM_CONTINUE;
995 return SILC_FSM_FINISH;
998 /********************************* INVITE ***********************************/
1000 /* Command INVITE. Invites specific client to join a channel. This is
1001 also used to mange the invite list of the channel. */
1003 SILC_FSM_STATE(silc_client_command_invite)
1005 SilcClientCommandContext cmd = fsm_context;
1006 SilcClientConnection conn = cmd->conn;
1007 SilcClient client = conn->client;
1008 SilcClientEntry client_entry = NULL;
1009 SilcChannelEntry channel = NULL;
1010 SilcBuffer clidp, chidp, args = NULL;
1011 SilcPublicKey pubkey = NULL;
1012 SilcDList clients = NULL;
1013 char *nickname = NULL, *name;
1014 char *invite = NULL;
1015 unsigned char action[1];
1017 if (cmd->argc < 2) {
1018 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1019 "Usage: /INVITE <channel> [<nickname>[@server>]"
1020 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1021 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1025 if (cmd->argv[1][0] == '*') {
1026 if (!conn->current_channel) {
1027 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1031 channel = conn->current_channel;
1032 silc_client_ref_channel(client, conn, channel);
1034 name = cmd->argv[1];
1036 channel = silc_client_get_channel(conn->client, conn, name);
1038 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1043 /* Parse the typed nickname. */
1044 if (cmd->argc == 3) {
1045 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1046 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1048 /* Find client entry */
1049 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1052 /* Resolve client information */
1053 SILC_FSM_CALL(silc_client_get_clients(
1054 client, conn, nickname, NULL,
1055 silc_client_command_resolve_continue,
1058 client_entry = silc_dlist_get(clients);
1060 if (cmd->argv[2][0] == '+')
1065 /* Check if it is public key file to be added to invite list */
1066 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1067 invite = cmd->argv[2];
1074 args = silc_buffer_alloc_size(2);
1075 silc_buffer_format(args,
1076 SILC_STR_UI_SHORT(1),
1079 chidp = silc_public_key_payload_encode(pubkey);
1080 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1081 silc_buffer_len(chidp), 2);
1082 silc_buffer_free(chidp);
1083 silc_pkcs_public_key_free(pubkey);
1085 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1089 /* Send the command */
1090 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1092 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1093 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1094 1, silc_buffer_datalen(chidp),
1095 2, silc_buffer_datalen(clidp),
1096 3, args ? action : NULL, args ? 1 : 0,
1097 4, silc_buffer_datalen(args));
1098 silc_buffer_free(clidp);
1100 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1101 1, silc_buffer_datalen(chidp),
1102 3, args ? action : NULL, args ? 1 : 0,
1103 4, silc_buffer_datalen(args));
1106 silc_buffer_free(chidp);
1107 silc_buffer_free(args);
1108 silc_free(nickname);
1109 silc_client_list_free(client, conn, clients);
1110 silc_client_unref_channel(client, conn, channel);
1112 /* Notify application */
1113 COMMAND(SILC_STATUS_OK);
1115 /** Wait for command reply */
1116 silc_fsm_next(fsm, silc_client_command_reply_wait);
1117 return SILC_FSM_CONTINUE;
1120 silc_free(nickname);
1121 return SILC_FSM_FINISH;
1124 /********************************** QUIT ************************************/
1126 /* Close the connection */
1128 SILC_FSM_STATE(silc_client_command_quit_final)
1130 SilcClientCommandContext cmd = fsm_context;
1131 SilcClientConnection conn = cmd->conn;
1133 SILC_LOG_DEBUG(("Quitting"));
1135 /* Notify application */
1136 COMMAND(SILC_STATUS_OK);
1138 /* Signal to close connection */
1139 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1140 if (!conn->internal->disconnected) {
1141 conn->internal->disconnected = TRUE;
1142 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1145 return SILC_FSM_FINISH;
1148 /* Command QUIT. Closes connection with current server. */
1150 SILC_FSM_STATE(silc_client_command_quit)
1152 SilcClientCommandContext cmd = fsm_context;
1153 SilcClientConnection conn = cmd->conn;
1156 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1157 1, cmd->argv[1], cmd->argv_lens[1]);
1159 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1161 /* Sleep for a while */
1164 /* We close the connection with a little timeout */
1165 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1166 return SILC_FSM_WAIT;
1169 /********************************** KILL ************************************/
1171 /* Command KILL. Router operator can use this command to remove an client
1172 fromthe SILC Network. */
1174 SILC_FSM_STATE(silc_client_command_kill)
1176 SilcClientCommandContext cmd = fsm_context;
1177 SilcClientConnection conn = cmd->conn;
1178 SilcClient client = conn->client;
1179 SilcBuffer idp, auth = NULL;
1180 SilcClientEntry target;
1182 char *nickname = NULL, *comment = NULL;
1184 if (cmd->argc < 2) {
1185 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1186 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1187 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1188 return SILC_FSM_FINISH;
1191 /* Parse the typed nickname. */
1192 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1193 return SILC_FSM_FINISH;
1195 /* Get the target client */
1196 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1198 /* Resolve client information */
1199 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1200 silc_client_command_resolve_continue,
1203 target = silc_dlist_get(clients);
1205 if (cmd->argc >= 3) {
1206 if (strcasecmp(cmd->argv[2], "-pubkey"))
1207 comment = cmd->argv[2];
1209 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1210 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1211 /* Encode the public key authentication payload */
1212 auth = silc_auth_public_key_auth_generate(conn->public_key,
1215 conn->internal->sha1hash,
1216 &target->id, SILC_ID_CLIENT);
1220 /* Send the KILL command to the server */
1221 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1222 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1223 1, silc_buffer_datalen(idp),
1224 2, comment, comment ? strlen(comment) : 0,
1225 3, silc_buffer_datalen(auth));
1226 silc_buffer_free(idp);
1227 silc_buffer_free(auth);
1228 silc_free(nickname);
1229 silc_client_list_free(client, conn, clients);
1231 /* Notify application */
1232 COMMAND(SILC_STATUS_OK);
1234 /** Wait for command reply */
1235 silc_fsm_next(fsm, silc_client_command_reply_wait);
1236 return SILC_FSM_CONTINUE;
1239 /********************************** INFO ************************************/
1241 /* Command INFO. Request information about specific server. If specific
1242 server is not provided the current server is used. */
1244 SILC_FSM_STATE(silc_client_command_info)
1246 SilcClientCommandContext cmd = fsm_context;
1247 SilcClientConnection conn = cmd->conn;
1249 /* Send the command */
1251 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1252 1, cmd->argv[1], cmd->argv_lens[1]);
1254 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1256 /* Notify application */
1257 COMMAND(SILC_STATUS_OK);
1259 /** Wait for command reply */
1260 silc_fsm_next(fsm, silc_client_command_reply_wait);
1261 return SILC_FSM_CONTINUE;
1264 /********************************** STATS ***********************************/
1266 /* Command STATS. Shows server and network statistics. */
1268 SILC_FSM_STATE(silc_client_command_stats)
1270 SilcClientCommandContext cmd = fsm_context;
1271 SilcClientConnection conn = cmd->conn;
1273 /* Send the command */
1274 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1275 1, silc_buffer_datalen(conn->internal->
1278 /* Notify application */
1279 COMMAND(SILC_STATUS_OK);
1281 /** Wait for command reply */
1282 silc_fsm_next(fsm, silc_client_command_reply_wait);
1283 return SILC_FSM_CONTINUE;
1286 /********************************** PING ************************************/
1288 /* Command PING. Sends ping to server. */
1290 SILC_FSM_STATE(silc_client_command_ping)
1292 SilcClientCommandContext cmd = fsm_context;
1293 SilcClientConnection conn = cmd->conn;
1295 if (cmd->argc < 2) {
1296 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1297 return SILC_FSM_FINISH;
1300 /* Send the command */
1301 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1302 1, silc_buffer_datalen(conn->internal->
1305 /* Save ping time */
1306 cmd->context = SILC_64_TO_PTR(silc_time());
1308 /* Notify application */
1309 COMMAND(SILC_STATUS_OK);
1311 /** Wait for command reply */
1312 silc_fsm_next(fsm, silc_client_command_reply_wait);
1313 return SILC_FSM_CONTINUE;
1316 /********************************** JOIN ************************************/
1318 /* Command JOIN. Joins to a channel. */
1320 SILC_FSM_STATE(silc_client_command_join)
1322 SilcClientCommandContext cmd2, cmd = fsm_context;
1323 SilcClientConnection conn = cmd->conn;
1324 SilcClient client = conn->client;
1325 SilcChannelEntry channel = NULL;
1326 SilcBuffer auth = NULL, cauth = NULL;
1327 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1328 int i, passphrase_len = 0;
1330 if (cmd->argc < 2) {
1331 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1335 /* See if we have joined to the requested channel already */
1336 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1337 if (channel && silc_client_on_channel(channel, conn->local_entry))
1340 /* If NICK command is active, wait for it to finish before sending JOIN.
1341 To avoid problems locally with changing IDs while joining, we do this. */
1342 silc_mutex_lock(conn->internal->lock);
1343 silc_list_start(conn->internal->pending_commands);
1344 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1345 if (cmd2->cmd == SILC_COMMAND_NICK) {
1346 silc_mutex_unlock(conn->internal->lock);
1347 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1348 return SILC_FSM_WAIT;
1351 silc_mutex_unlock(conn->internal->lock);
1353 if (cmd->argv_lens[1] > 256)
1354 cmd->argv_lens[1] = 256;
1356 name = cmd->argv[1];
1358 for (i = 2; i < cmd->argc; i++) {
1359 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1360 cipher = cmd->argv[++i];
1361 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1362 hmac = cmd->argv[++i];
1363 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1364 auth = silc_auth_public_key_auth_generate(conn->public_key,
1367 conn->internal->sha1hash,
1370 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1371 SilcPublicKey pubkey = conn->public_key;
1372 SilcPrivateKey privkey = conn->private_key;
1373 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1376 if (cmd->argc >= i + 3) {
1378 if (cmd->argc >= i + 4) {
1379 pass = cmd->argv[i + 3];
1382 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1383 &pubkey, &privkey)) {
1384 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1385 "Could not load key pair, check your arguments");
1386 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1392 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1393 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1395 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1396 memcpy(pubdata, pkhash, 20);
1397 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1399 conn->internal->sha1hash,
1402 memset(pubdata, 0, 128);
1405 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1406 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1407 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1408 cmd->argv_lens[i], 0);
1409 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1410 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1411 0, pu8, passphrase_len);
1414 passphrase = strdup(cmd->argv[i]);
1415 passphrase_len = cmd->argv_lens[i];
1420 /* Send JOIN command to the server */
1421 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1422 1, name, strlen(name),
1423 2, silc_buffer_datalen(conn->internal->
1425 3, passphrase, passphrase_len,
1426 4, cipher, cipher ? strlen(cipher) : 0,
1427 5, hmac, hmac ? strlen(hmac) : 0,
1428 6, silc_buffer_datalen(auth),
1429 7, silc_buffer_datalen(cauth));
1431 silc_buffer_free(auth);
1432 silc_buffer_free(cauth);
1434 memset(passphrase, 0, strlen(passphrase));
1435 silc_free(passphrase);
1436 silc_client_unref_channel(client, conn, channel);
1438 /* Notify application */
1439 COMMAND(SILC_STATUS_OK);
1441 /** Wait for command reply */
1442 silc_fsm_next(fsm, silc_client_command_reply_wait);
1443 return SILC_FSM_CONTINUE;
1446 silc_client_unref_channel(client, conn, channel);
1447 return SILC_FSM_FINISH;
1450 /********************************** MOTD ************************************/
1452 /* MOTD command. Requests motd from server. */
1454 SILC_FSM_STATE(silc_client_command_motd)
1456 SilcClientCommandContext cmd = fsm_context;
1457 SilcClientConnection conn = cmd->conn;
1459 if (cmd->argc < 1 || cmd->argc > 2) {
1460 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1461 "Usage: /MOTD [<server>]");
1462 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1463 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1464 return SILC_FSM_FINISH;
1467 /* Send the command */
1469 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1470 1, conn->remote_host,
1471 strlen(conn->remote_host));
1473 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1474 1, cmd->argv[1], cmd->argv_lens[1]);
1476 /* Notify application */
1477 COMMAND(SILC_STATUS_OK);
1479 /** Wait for command reply */
1480 silc_fsm_next(fsm, silc_client_command_reply_wait);
1481 return SILC_FSM_CONTINUE;
1484 /********************************** UMODE ***********************************/
1486 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1487 modes as client cannot set itself server/router operator privileges. */
1489 SILC_FSM_STATE(silc_client_command_umode)
1491 SilcClientCommandContext cmd = fsm_context;
1492 SilcClientConnection conn = cmd->conn;
1493 unsigned char *cp, modebuf[4];
1494 SilcUInt32 mode, add, len;
1497 if (cmd->argc < 2) {
1498 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1499 "Usage: /UMODE +|-<modes>");
1500 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1501 return SILC_FSM_FINISH;
1504 mode = conn->local_entry->mode;
1506 /* Are we adding or removing mode */
1507 if (cmd->argv[1][0] == '-')
1513 cp = cmd->argv[1] + 1;
1515 for (i = 0; i < len; i++) {
1520 mode |= SILC_UMODE_SERVER_OPERATOR;
1521 mode |= SILC_UMODE_ROUTER_OPERATOR;
1522 mode |= SILC_UMODE_GONE;
1523 mode |= SILC_UMODE_INDISPOSED;
1524 mode |= SILC_UMODE_BUSY;
1525 mode |= SILC_UMODE_PAGE;
1526 mode |= SILC_UMODE_HYPER;
1527 mode |= SILC_UMODE_ROBOT;
1528 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1529 mode |= SILC_UMODE_REJECT_WATCHING;
1531 mode = SILC_UMODE_NONE;
1536 mode |= SILC_UMODE_SERVER_OPERATOR;
1538 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1542 mode |= SILC_UMODE_ROUTER_OPERATOR;
1544 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1548 mode |= SILC_UMODE_GONE;
1550 mode &= ~SILC_UMODE_GONE;
1554 mode |= SILC_UMODE_INDISPOSED;
1556 mode &= ~SILC_UMODE_INDISPOSED;
1560 mode |= SILC_UMODE_BUSY;
1562 mode &= ~SILC_UMODE_BUSY;
1566 mode |= SILC_UMODE_PAGE;
1568 mode &= ~SILC_UMODE_PAGE;
1572 mode |= SILC_UMODE_HYPER;
1574 mode &= ~SILC_UMODE_HYPER;
1578 mode |= SILC_UMODE_ROBOT;
1580 mode &= ~SILC_UMODE_ROBOT;
1584 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1586 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1590 mode |= SILC_UMODE_REJECT_WATCHING;
1592 mode &= ~SILC_UMODE_REJECT_WATCHING;
1596 mode |= SILC_UMODE_BLOCK_INVITE;
1598 mode &= ~SILC_UMODE_BLOCK_INVITE;
1601 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1602 return SILC_FSM_FINISH;
1607 SILC_PUT32_MSB(mode, modebuf);
1609 /* Send the command */
1610 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1611 1, silc_buffer_datalen(conn->internal->
1613 2, modebuf, sizeof(modebuf));
1615 /* Notify application */
1616 COMMAND(SILC_STATUS_OK);
1618 /** Wait for command reply */
1619 silc_fsm_next(fsm, silc_client_command_reply_wait);
1620 return SILC_FSM_CONTINUE;
1623 /********************************** CMODE ***********************************/
1625 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1626 can be set several at once. Those modes that require argument must be set
1627 separately (unless set with modes that does not require arguments). */
1629 SILC_FSM_STATE(silc_client_command_cmode)
1631 SilcClientCommandContext cmd = fsm_context;
1632 SilcClientConnection conn = cmd->conn;
1633 SilcClient client = conn->client;
1634 SilcChannelEntry channel = NULL;
1635 SilcBuffer chidp, auth = NULL, pk = NULL;
1636 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1637 SilcUInt32 mode, add, type, len, arg_len = 0;
1640 if (cmd->argc < 3) {
1641 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1642 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1643 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1647 if (cmd->argv[1][0] == '*') {
1648 if (!conn->current_channel) {
1649 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1653 channel = conn->current_channel;
1654 silc_client_ref_channel(client, conn, channel);
1656 name = cmd->argv[1];
1658 channel = silc_client_get_channel(conn->client, conn, name);
1660 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1665 mode = channel->mode;
1667 /* Are we adding or removing mode */
1668 if (cmd->argv[2][0] == '-')
1673 /* Argument type to be sent to server */
1677 cp = cmd->argv[2] + 1;
1679 for (i = 0; i < len; i++) {
1683 mode |= SILC_CHANNEL_MODE_PRIVATE;
1685 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1689 mode |= SILC_CHANNEL_MODE_SECRET;
1691 mode &= ~SILC_CHANNEL_MODE_SECRET;
1695 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1697 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1701 mode |= SILC_CHANNEL_MODE_INVITE;
1703 mode &= ~SILC_CHANNEL_MODE_INVITE;
1707 mode |= SILC_CHANNEL_MODE_TOPIC;
1709 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1713 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1715 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1719 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1721 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1726 mode |= SILC_CHANNEL_MODE_ULIMIT;
1728 if (cmd->argc < 4) {
1729 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1730 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1731 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1734 ll = atoi(cmd->argv[3]);
1735 SILC_PUT32_MSB(ll, tmp);
1739 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1744 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1746 if (cmd->argc < 4) {
1747 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1748 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1749 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1753 arg_len = cmd->argv_lens[3];
1755 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1760 mode |= SILC_CHANNEL_MODE_CIPHER;
1762 if (cmd->argc < 4) {
1763 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1764 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1765 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1769 arg_len = cmd->argv_lens[3];
1771 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1776 mode |= SILC_CHANNEL_MODE_HMAC;
1778 if (cmd->argc < 4) {
1779 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1780 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1781 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1785 arg_len = cmd->argv_lens[3];
1787 mode &= ~SILC_CHANNEL_MODE_HMAC;
1792 SilcPublicKey pubkey = conn->public_key;
1793 SilcPrivateKey privkey = conn->private_key;
1795 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1798 if (cmd->argc >= 5) {
1801 pass = cmd->argv[5];
1802 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1803 &pubkey, &privkey)) {
1804 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1805 "Could not load key pair, check your arguments");
1806 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1811 pk = silc_public_key_payload_encode(pubkey);
1812 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1814 conn->internal->sha1hash,
1817 arg = silc_buffer_data(auth);
1818 arg_len = silc_buffer_len(auth);
1820 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1826 SilcBool chadd = FALSE;
1827 SilcPublicKey chpk = NULL;
1829 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1832 if (cmd->argc == 3) {
1833 /* Send empty command to receive the public key list. */
1834 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1835 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1837 1, silc_buffer_datalen(chidp));
1838 silc_buffer_free(chidp);
1840 /* Notify application */
1841 COMMAND(SILC_STATUS_OK);
1845 if (cmd->argc >= 4) {
1846 auth = silc_buffer_alloc_size(2);
1847 silc_buffer_format(auth,
1848 SILC_STR_UI_SHORT(cmd->argc - 3),
1852 for (k = 3; k < cmd->argc; k++) {
1853 if (cmd->argv[k][0] == '+')
1855 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1856 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1857 "Could not load public key %s, check the filename",
1859 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1860 silc_buffer_free(auth);
1865 pk = silc_public_key_payload_encode(chpk);
1866 auth = silc_argument_payload_encode_one(auth,
1867 silc_buffer_datalen(pk),
1868 chadd ? 0x00 : 0x01);
1869 silc_pkcs_public_key_free(chpk);
1870 silc_buffer_free(pk);
1875 arg = silc_buffer_data(auth);
1876 arg_len = silc_buffer_len(auth);
1878 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1882 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1888 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1889 SILC_PUT32_MSB(mode, modebuf);
1891 /* Send the command. We support sending only one mode at once that
1892 requires an argument. */
1894 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1895 1, silc_buffer_datalen(chidp),
1896 2, modebuf, sizeof(modebuf),
1898 8, silc_buffer_datalen(pk));
1900 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1901 1, silc_buffer_datalen(chidp),
1902 2, modebuf, sizeof(modebuf));
1905 silc_buffer_free(chidp);
1906 silc_buffer_free(auth);
1907 silc_buffer_free(pk);
1908 silc_client_unref_channel(client, conn, channel);
1910 /* Notify application */
1911 COMMAND(SILC_STATUS_OK);
1913 /** Wait for command reply */
1914 silc_fsm_next(fsm, silc_client_command_reply_wait);
1915 return SILC_FSM_CONTINUE;
1918 silc_client_unref_channel(client, conn, channel);
1919 return SILC_FSM_FINISH;
1922 /********************************* CUMODE ***********************************/
1924 /* CUMODE command. Changes client's mode on a channel. */
1926 SILC_FSM_STATE(silc_client_command_cumode)
1928 SilcClientCommandContext cmd = fsm_context;
1929 SilcClientConnection conn = cmd->conn;
1930 SilcClient client = conn->client;
1931 SilcChannelEntry channel = NULL;
1932 SilcChannelUser chu;
1933 SilcClientEntry client_entry;
1934 SilcBuffer clidp, chidp, auth = NULL;
1935 SilcDList clients = NULL;
1936 unsigned char *name, *cp, modebuf[4];
1937 SilcUInt32 mode = 0, add, len;
1938 char *nickname = NULL;
1941 if (cmd->argc < 4) {
1942 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1943 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1944 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1948 if (cmd->argv[1][0] == '*') {
1949 if (!conn->current_channel) {
1950 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1954 channel = conn->current_channel;
1955 silc_client_ref_channel(client, conn, channel);
1957 name = cmd->argv[1];
1959 channel = silc_client_get_channel(conn->client, conn, name);
1961 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1966 /* Parse the typed nickname. */
1967 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1969 /* Find client entry */
1970 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
1972 /* Resolve client information */
1973 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1974 silc_client_command_resolve_continue,
1977 client_entry = silc_dlist_get(clients);
1979 /* Get the current mode */
1980 chu = silc_client_on_channel(channel, client_entry);
1984 /* Are we adding or removing mode */
1985 if (cmd->argv[2][0] == '-')
1991 cp = cmd->argv[2] + 1;
1993 for (i = 0; i < len; i++) {
1997 mode |= SILC_CHANNEL_UMODE_CHANFO;
1998 mode |= SILC_CHANNEL_UMODE_CHANOP;
1999 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2000 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2001 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2003 mode = SILC_CHANNEL_UMODE_NONE;
2008 SilcPublicKey pubkey = conn->public_key;
2009 SilcPrivateKey privkey = conn->private_key;
2011 if (cmd->argc >= 6) {
2014 pass = cmd->argv[6];
2015 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2016 &pubkey, &privkey)) {
2017 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2018 "Could not load key pair, check your arguments");
2019 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2024 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
2026 conn->internal->sha1hash,
2029 mode |= SILC_CHANNEL_UMODE_CHANFO;
2031 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2036 mode |= SILC_CHANNEL_UMODE_CHANOP;
2038 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2042 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2044 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2048 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2050 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2054 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2056 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2060 mode |= SILC_CHANNEL_UMODE_QUIET;
2062 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2065 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2071 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2072 SILC_PUT32_MSB(mode, modebuf);
2073 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2075 /* Send the command packet. We support sending only one mode at once
2076 that requires an argument. */
2077 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2078 1, silc_buffer_datalen(chidp),
2080 3, silc_buffer_datalen(clidp),
2081 4, silc_buffer_datalen(auth));
2083 silc_buffer_free(chidp);
2084 silc_buffer_free(clidp);
2086 silc_buffer_free(auth);
2087 silc_free(nickname);
2088 silc_client_list_free(client, conn, clients);
2089 silc_client_unref_channel(client, conn, channel);
2091 /* Notify application */
2092 COMMAND(SILC_STATUS_OK);
2094 /** Wait for command reply */
2095 silc_fsm_next(fsm, silc_client_command_reply_wait);
2096 return SILC_FSM_CONTINUE;
2099 silc_client_unref_channel(client, conn, channel);
2100 silc_client_list_free(client, conn, clients);
2101 silc_free(nickname);
2102 return SILC_FSM_FINISH;
2105 /********************************** KICK ************************************/
2107 /* KICK command. Kicks a client out of channel. */
2109 SILC_FSM_STATE(silc_client_command_kick)
2111 SilcClientCommandContext cmd = fsm_context;
2112 SilcClientConnection conn = cmd->conn;
2113 SilcClient client = conn->client;
2114 SilcChannelEntry channel = NULL;
2115 SilcBuffer idp, idp2;
2116 SilcClientEntry target;
2117 SilcDList clients = NULL;
2120 if (cmd->argc < 3) {
2121 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2122 "Usage: /KICK <channel> <nickname> [<comment>]");
2123 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2127 if (cmd->argv[1][0] == '*') {
2128 if (!conn->current_channel) {
2129 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2132 name = conn->current_channel->channel_name;
2134 name = cmd->argv[1];
2137 if (!conn->current_channel) {
2138 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2142 /* Get the Channel ID of the channel */
2143 channel = silc_client_get_channel(conn->client, conn, name);
2145 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2149 /* Get the target client */
2150 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
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_client_list_free(client, conn, clients);
2175 silc_client_unref_channel(client, conn, channel);
2177 /* Notify application */
2178 COMMAND(SILC_STATUS_OK);
2180 /** Wait for command reply */
2181 silc_fsm_next(fsm, silc_client_command_reply_wait);
2182 return SILC_FSM_CONTINUE;
2185 silc_client_unref_channel(client, conn, channel);
2186 return SILC_FSM_FINISH;
2189 /***************************** OPER & SILCOPER ******************************/
2192 unsigned char *passphrase;
2193 SilcUInt32 passphrase_len;
2194 } *SilcClientCommandOper;
2196 /* Ask passphrase callback */
2198 static void silc_client_command_oper_cb(unsigned char *data,
2199 SilcUInt32 data_len, void *context)
2201 SilcClientCommandContext cmd = context;
2202 SilcClientCommandOper oper = cmd->context;
2204 if (data && data_len)
2205 oper->passphrase = silc_memdup(data, data_len);
2206 oper->passphrase_len = data_len;
2209 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2212 /* Send OPER/SILCOPER command */
2214 SILC_FSM_STATE(silc_client_command_oper_send)
2216 SilcClientCommandContext cmd = fsm_context;
2217 SilcClientConnection conn = cmd->conn;
2218 SilcClientCommandOper oper = cmd->context;
2221 if (!oper || !oper->passphrase) {
2222 /* Encode the public key authentication payload */
2223 auth = silc_auth_public_key_auth_generate(conn->public_key,
2226 conn->internal->hash,
2230 /* Encode the password authentication payload */
2231 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2232 oper->passphrase, oper->passphrase_len);
2235 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2236 1, cmd->argv[1], strlen(cmd->argv[1]),
2237 2, silc_buffer_datalen(auth));
2239 silc_buffer_clear(auth);
2240 silc_buffer_free(auth);
2242 silc_free(oper->passphrase);
2246 /* Notify application */
2247 COMMAND(SILC_STATUS_OK);
2249 /** Wait for command reply */
2250 silc_fsm_next(fsm, silc_client_command_reply_wait);
2251 return SILC_FSM_CONTINUE;
2254 /* OPER command. Used to obtain server operator privileges. */
2256 SILC_FSM_STATE(silc_client_command_oper)
2258 SilcClientCommandContext cmd = fsm_context;
2259 SilcClientConnection conn = cmd->conn;
2260 SilcClientCommandOper oper;
2262 if (cmd->argc < 2) {
2263 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2264 "Usage: /OPER <username> [-pubkey]");
2265 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2266 return SILC_FSM_FINISH;
2269 silc_fsm_next(fsm, silc_client_command_oper_send);
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 return SILC_FSM_CONTINUE;
2285 /* SILCOPER command. Used to obtain router operator privileges. */
2287 SILC_FSM_STATE(silc_client_command_silcoper)
2289 SilcClientCommandContext cmd = fsm_context;
2290 SilcClientConnection conn = cmd->conn;
2291 SilcClientCommandOper oper;
2293 if (cmd->argc < 2) {
2294 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2295 "Usage: /SILCOPER <username> [-pubkey]");
2296 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2297 return SILC_FSM_FINISH;
2300 silc_fsm_next(fsm, silc_client_command_oper_send);
2302 /* Get passphrase */
2303 if (cmd->argc < 3) {
2304 oper = silc_calloc(1, sizeof(*oper));
2306 return SILC_FSM_FINISH;
2307 cmd->context = oper;
2308 SILC_FSM_CALL(conn->client->internal->
2309 ops->ask_passphrase(conn->client, conn,
2310 silc_client_command_oper_cb, cmd));
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;
2627 if (cmd->argc < 2) {
2628 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2629 "Usage: /GETKEY <nickname or server name>");
2630 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2631 return SILC_FSM_FINISH;
2634 /* Find client entry */
2635 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2637 /* Check whether user requested server */
2638 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2639 if (!server_entry) {
2640 if (cmd->resolved) {
2641 /* Resolving didn't find anything. We should never get here as
2642 errors are handled in the resolving callback. */
2643 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2644 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2645 return SILC_FSM_FINISH;
2648 /* No client or server exist with this name, query for both. */
2649 cmd->resolved = TRUE;
2650 SILC_FSM_CALL(silc_client_command_send(client, conn,
2651 SILC_COMMAND_IDENTIFY,
2652 silc_client_command_continue,
2655 strlen(cmd->argv[1]),
2657 strlen(cmd->argv[1])));
2660 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2661 silc_client_unref_server(client, conn, server_entry);
2663 client_entry = silc_dlist_get(clients);
2664 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2665 silc_client_list_free(client, conn, clients);
2668 /* Send the commmand */
2669 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2670 1, silc_buffer_datalen(idp));
2672 silc_buffer_free(idp);
2674 /* Notify application */
2675 COMMAND(SILC_STATUS_OK);
2677 /** Wait for command reply */
2678 silc_fsm_next(fsm, silc_client_command_reply_wait);
2679 return SILC_FSM_CONTINUE;
2682 /********************************* SERVICE **********************************/
2684 /* Command SERVICE. Negotiates service agreement with server. */
2685 /* XXX incomplete */
2687 SILC_FSM_STATE(silc_client_command_service)
2689 SilcClientCommandContext cmd = fsm_context;
2691 SilcClientConnection conn = cmd->conn;
2695 if (cmd->argc < 2) {
2696 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2697 "Usage: /SERVICE [<service name>] [-pubkey]");
2698 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2699 return SILC_FSM_FINISH;
2702 name = cmd->argv[1];
2704 /* Send SERVICE command to the server */
2705 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2706 ++conn->cmd_ident, 1,
2707 1, name, strlen(name));
2708 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2709 NULL, 0, NULL, NULL, buffer->data,
2711 silc_buffer_free(buffer);
2714 /* Notify application */
2715 COMMAND(SILC_STATUS_OK);
2717 /** Wait for command reply */
2718 silc_fsm_next(fsm, silc_client_command_reply_wait);
2719 return SILC_FSM_CONTINUE;
2722 /* Register all default commands provided by the client library for the
2725 void silc_client_commands_register(SilcClient client)
2727 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2730 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2731 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2732 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2733 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2734 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2735 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2736 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2737 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2738 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2739 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2740 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2741 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2742 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2743 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2744 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2745 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2746 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2747 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2748 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2749 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2750 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2751 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2752 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2753 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2754 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2755 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2756 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2759 /* Unregister all commands. */
2761 void silc_client_commands_unregister(SilcClient client)
2763 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2764 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2765 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2766 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2767 SILC_CLIENT_CMDU(list, LIST, "LIST");
2768 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2769 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2770 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2771 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2772 SILC_CLIENT_CMDU(info, INFO, "INFO");
2773 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2774 SILC_CLIENT_CMDU(ping, PING, "PING");
2775 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2776 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2777 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2778 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2779 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2780 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2781 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2782 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2783 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2784 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2785 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2786 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2787 SILC_CLIENT_CMDU(users, USERS, "USERS");
2788 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2789 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2792 /****************** Client Side Incoming Command Handling *******************/
2794 /* Reply to WHOIS command from server */
2796 static void silc_client_command_process_whois(SilcClient client,
2797 SilcClientConnection conn,
2798 SilcCommandPayload payload,
2799 SilcArgumentPayload args)
2804 SilcBuffer buffer, packet;
2806 SILC_LOG_DEBUG(("Received WHOIS command"));
2808 /* Try to take the Requested Attributes */
2809 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2813 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2817 /* Process requested attributes */
2818 buffer = silc_client_attributes_process(client, conn, attrs);
2820 silc_attribute_payload_list_free(attrs);
2824 /* Send the attributes back in COMMAND_REPLY packet */
2826 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2828 silc_command_get_ident(payload),
2829 1, 11, buffer->data,
2830 silc_buffer_len(buffer));
2832 silc_buffer_free(buffer);
2836 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
2838 silc_packet_send(conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
2839 silc_buffer_datalen(packet));
2841 silc_buffer_free(packet);
2842 silc_buffer_free(buffer);
2845 /* Client is able to receive some command packets even though they are
2846 special case. Server may send WHOIS command to the client to retrieve
2847 Requested Attributes information for WHOIS query the server is
2848 processing. This function currently handles only the WHOIS command,
2849 but if in the future more commands may arrive then this can be made
2850 to support other commands too. */
2852 SILC_FSM_STATE(silc_client_command)
2854 SilcClientConnection conn = fsm_context;
2855 SilcClient client = conn->client;
2856 SilcPacket packet = state_context;
2857 SilcCommandPayload payload;
2858 SilcCommand command;
2859 SilcArgumentPayload args;
2861 /* Get command payload from packet */
2862 payload = silc_command_payload_parse(packet->buffer.data,
2863 silc_buffer_len(&packet->buffer));
2865 SILC_LOG_DEBUG(("Bad command packet"));
2866 return SILC_FSM_FINISH;
2870 args = silc_command_get_args(payload);
2872 /* Get the command */
2873 command = silc_command_get(payload);
2876 case SILC_COMMAND_WHOIS:
2877 /* Ignore everything if requested by application */
2878 if (conn->internal->params.ignore_requested_attributes)
2881 silc_client_command_process_whois(client, conn, payload, args);
2888 silc_command_payload_free(payload);
2889 return SILC_FSM_FINISH;