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);
1839 silc_client_unref_channel(client, conn, channel);
1841 /* Notify application */
1842 COMMAND(SILC_STATUS_OK);
1844 /** Wait for command reply */
1845 silc_fsm_next(fsm, silc_client_command_reply_wait);
1846 return SILC_FSM_CONTINUE;
1849 if (cmd->argc >= 4) {
1850 auth = silc_buffer_alloc_size(2);
1851 silc_buffer_format(auth,
1852 SILC_STR_UI_SHORT(cmd->argc - 3),
1856 for (k = 3; k < cmd->argc; k++) {
1857 if (cmd->argv[k][0] == '+')
1859 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1860 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1861 "Could not load public key %s, check the filename",
1863 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1864 silc_buffer_free(auth);
1869 pk = silc_public_key_payload_encode(chpk);
1870 auth = silc_argument_payload_encode_one(auth,
1871 silc_buffer_datalen(pk),
1872 chadd ? 0x00 : 0x01);
1873 silc_pkcs_public_key_free(chpk);
1874 silc_buffer_free(pk);
1879 arg = silc_buffer_data(auth);
1880 arg_len = silc_buffer_len(auth);
1882 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1886 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1892 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1893 SILC_PUT32_MSB(mode, modebuf);
1895 /* Send the command. We support sending only one mode at once that
1896 requires an argument. */
1898 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1899 1, silc_buffer_datalen(chidp),
1900 2, modebuf, sizeof(modebuf),
1902 8, silc_buffer_datalen(pk));
1904 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1905 1, silc_buffer_datalen(chidp),
1906 2, modebuf, sizeof(modebuf));
1909 silc_buffer_free(chidp);
1910 silc_buffer_free(auth);
1911 silc_buffer_free(pk);
1912 silc_client_unref_channel(client, conn, channel);
1914 /* Notify application */
1915 COMMAND(SILC_STATUS_OK);
1917 /** Wait for command reply */
1918 silc_fsm_next(fsm, silc_client_command_reply_wait);
1919 return SILC_FSM_CONTINUE;
1922 silc_client_unref_channel(client, conn, channel);
1923 return SILC_FSM_FINISH;
1926 /********************************* CUMODE ***********************************/
1928 /* CUMODE command. Changes client's mode on a channel. */
1930 SILC_FSM_STATE(silc_client_command_cumode)
1932 SilcClientCommandContext cmd = fsm_context;
1933 SilcClientConnection conn = cmd->conn;
1934 SilcClient client = conn->client;
1935 SilcChannelEntry channel = NULL;
1936 SilcChannelUser chu;
1937 SilcClientEntry client_entry;
1938 SilcBuffer clidp, chidp, auth = NULL;
1939 SilcDList clients = NULL;
1940 unsigned char *name, *cp, modebuf[4];
1941 SilcUInt32 mode = 0, add, len;
1942 char *nickname = NULL;
1945 if (cmd->argc < 4) {
1946 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1947 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1948 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1952 if (cmd->argv[1][0] == '*') {
1953 if (!conn->current_channel) {
1954 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1958 channel = conn->current_channel;
1959 silc_client_ref_channel(client, conn, channel);
1961 name = cmd->argv[1];
1963 channel = silc_client_get_channel(conn->client, conn, name);
1965 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1970 /* Parse the typed nickname. */
1971 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1973 /* Find client entry */
1974 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
1976 /* Resolve client information */
1977 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1978 silc_client_command_resolve_continue,
1981 client_entry = silc_dlist_get(clients);
1983 /* Get the current mode */
1984 chu = silc_client_on_channel(channel, client_entry);
1988 /* Are we adding or removing mode */
1989 if (cmd->argv[2][0] == '-')
1995 cp = cmd->argv[2] + 1;
1997 for (i = 0; i < len; i++) {
2001 mode |= SILC_CHANNEL_UMODE_CHANFO;
2002 mode |= SILC_CHANNEL_UMODE_CHANOP;
2003 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2004 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2005 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2007 mode = SILC_CHANNEL_UMODE_NONE;
2012 SilcPublicKey pubkey = conn->public_key;
2013 SilcPrivateKey privkey = conn->private_key;
2015 if (cmd->argc >= 6) {
2018 pass = cmd->argv[6];
2019 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2020 &pubkey, &privkey)) {
2021 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2022 "Could not load key pair, check your arguments");
2023 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2028 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
2030 conn->internal->sha1hash,
2033 mode |= SILC_CHANNEL_UMODE_CHANFO;
2035 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2040 mode |= SILC_CHANNEL_UMODE_CHANOP;
2042 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2046 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2048 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2052 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2054 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2058 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2060 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2064 mode |= SILC_CHANNEL_UMODE_QUIET;
2066 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2069 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2075 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2076 SILC_PUT32_MSB(mode, modebuf);
2077 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2079 /* Send the command packet. We support sending only one mode at once
2080 that requires an argument. */
2081 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2082 1, silc_buffer_datalen(chidp),
2084 3, silc_buffer_datalen(clidp),
2085 4, silc_buffer_datalen(auth));
2087 silc_buffer_free(chidp);
2088 silc_buffer_free(clidp);
2090 silc_buffer_free(auth);
2091 silc_free(nickname);
2092 silc_client_list_free(client, conn, clients);
2093 silc_client_unref_channel(client, conn, channel);
2095 /* Notify application */
2096 COMMAND(SILC_STATUS_OK);
2098 /** Wait for command reply */
2099 silc_fsm_next(fsm, silc_client_command_reply_wait);
2100 return SILC_FSM_CONTINUE;
2103 silc_client_unref_channel(client, conn, channel);
2104 silc_client_list_free(client, conn, clients);
2105 silc_free(nickname);
2106 return SILC_FSM_FINISH;
2109 /********************************** KICK ************************************/
2111 /* KICK command. Kicks a client out of channel. */
2113 SILC_FSM_STATE(silc_client_command_kick)
2115 SilcClientCommandContext cmd = fsm_context;
2116 SilcClientConnection conn = cmd->conn;
2117 SilcClient client = conn->client;
2118 SilcChannelEntry channel = NULL;
2119 SilcBuffer idp, idp2;
2120 SilcClientEntry target;
2121 SilcDList clients = NULL;
2124 if (cmd->argc < 3) {
2125 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2126 "Usage: /KICK <channel> <nickname> [<comment>]");
2127 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2131 if (cmd->argv[1][0] == '*') {
2132 if (!conn->current_channel) {
2133 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2136 name = conn->current_channel->channel_name;
2138 name = cmd->argv[1];
2141 if (!conn->current_channel) {
2142 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2146 /* Get the Channel ID of the channel */
2147 channel = silc_client_get_channel(conn->client, conn, name);
2149 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2153 /* Get the target client */
2154 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2156 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2157 "No such client: %s", cmd->argv[2]);
2158 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2161 target = silc_dlist_get(clients);
2163 /* Send KICK command to the server */
2164 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2165 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2167 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2168 1, silc_buffer_datalen(idp),
2169 2, silc_buffer_datalen(idp2));
2171 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2172 1, silc_buffer_datalen(idp),
2173 2, silc_buffer_datalen(idp2),
2174 3, cmd->argv[3], strlen(cmd->argv[3]));
2176 silc_buffer_free(idp);
2177 silc_buffer_free(idp2);
2178 silc_client_list_free(client, conn, clients);
2179 silc_client_unref_channel(client, conn, channel);
2181 /* Notify application */
2182 COMMAND(SILC_STATUS_OK);
2184 /** Wait for command reply */
2185 silc_fsm_next(fsm, silc_client_command_reply_wait);
2186 return SILC_FSM_CONTINUE;
2189 silc_client_unref_channel(client, conn, channel);
2190 return SILC_FSM_FINISH;
2193 /***************************** OPER & SILCOPER ******************************/
2196 unsigned char *passphrase;
2197 SilcUInt32 passphrase_len;
2198 } *SilcClientCommandOper;
2200 /* Ask passphrase callback */
2202 static void silc_client_command_oper_cb(const unsigned char *data,
2203 SilcUInt32 data_len, void *context)
2205 SilcClientCommandContext cmd = context;
2206 SilcClientCommandOper oper = cmd->context;
2208 if (data && data_len)
2209 oper->passphrase = silc_memdup(data, data_len);
2210 oper->passphrase_len = data_len;
2213 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2216 /* Send OPER/SILCOPER command */
2218 SILC_FSM_STATE(silc_client_command_oper_send)
2220 SilcClientCommandContext cmd = fsm_context;
2221 SilcClientConnection conn = cmd->conn;
2222 SilcClientCommandOper oper = cmd->context;
2225 if (!oper || !oper->passphrase) {
2226 /* Encode the public key authentication payload */
2227 auth = silc_auth_public_key_auth_generate(conn->public_key,
2230 conn->internal->hash,
2234 /* Encode the password authentication payload */
2235 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2236 oper->passphrase, oper->passphrase_len);
2239 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2240 1, cmd->argv[1], strlen(cmd->argv[1]),
2241 2, silc_buffer_datalen(auth));
2243 silc_buffer_clear(auth);
2244 silc_buffer_free(auth);
2246 silc_free(oper->passphrase);
2250 /* Notify application */
2251 COMMAND(SILC_STATUS_OK);
2253 /** Wait for command reply */
2254 silc_fsm_next(fsm, silc_client_command_reply_wait);
2255 return SILC_FSM_CONTINUE;
2258 /* OPER command. Used to obtain server operator privileges. */
2260 SILC_FSM_STATE(silc_client_command_oper)
2262 SilcClientCommandContext cmd = fsm_context;
2263 SilcClientConnection conn = cmd->conn;
2264 SilcClientCommandOper oper;
2266 if (cmd->argc < 2) {
2267 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2268 "Usage: /OPER <username> [-pubkey]");
2269 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2270 return SILC_FSM_FINISH;
2273 silc_fsm_next(fsm, silc_client_command_oper_send);
2275 /* Get passphrase */
2276 if (cmd->argc < 3) {
2277 oper = silc_calloc(1, sizeof(*oper));
2279 return SILC_FSM_FINISH;
2280 cmd->context = oper;
2281 SILC_FSM_CALL(conn->client->internal->
2282 ops->ask_passphrase(conn->client, conn,
2283 silc_client_command_oper_cb, cmd));
2286 return SILC_FSM_CONTINUE;
2289 /* SILCOPER command. Used to obtain router operator privileges. */
2291 SILC_FSM_STATE(silc_client_command_silcoper)
2293 SilcClientCommandContext cmd = fsm_context;
2294 SilcClientConnection conn = cmd->conn;
2295 SilcClientCommandOper oper;
2297 if (cmd->argc < 2) {
2298 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2299 "Usage: /SILCOPER <username> [-pubkey]");
2300 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2301 return SILC_FSM_FINISH;
2304 silc_fsm_next(fsm, silc_client_command_oper_send);
2306 /* Get passphrase */
2307 if (cmd->argc < 3) {
2308 oper = silc_calloc(1, sizeof(*oper));
2310 return SILC_FSM_FINISH;
2311 cmd->context = oper;
2312 SILC_FSM_CALL(conn->client->internal->
2313 ops->ask_passphrase(conn->client, conn,
2314 silc_client_command_oper_cb, cmd));
2317 return SILC_FSM_CONTINUE;
2320 /*********************************** BAN ************************************/
2322 /* Command BAN. This is used to manage the ban list of the channel. */
2324 SILC_FSM_STATE(silc_client_command_ban)
2326 SilcClientCommandContext cmd = fsm_context;
2327 SilcClientConnection conn = cmd->conn;
2328 SilcClient client = conn->client;
2329 SilcChannelEntry channel;
2330 SilcBuffer chidp, args = NULL;
2331 char *name, *ban = NULL;
2332 unsigned char action[1];
2333 SilcPublicKey pubkey = NULL;
2335 if (cmd->argc < 2) {
2336 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2337 "Usage: /BAN <channel> "
2338 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2339 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2343 if (cmd->argv[1][0] == '*') {
2344 if (!conn->current_channel) {
2345 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2349 channel = conn->current_channel;
2350 silc_client_ref_channel(client, conn, channel);
2352 name = cmd->argv[1];
2354 channel = silc_client_get_channel(conn->client, conn, name);
2356 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2361 if (cmd->argc == 3) {
2362 if (cmd->argv[2][0] == '+')
2367 /* Check if it is public key file to be added to invite list */
2368 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2375 args = silc_buffer_alloc_size(2);
2376 silc_buffer_format(args,
2377 SILC_STR_UI_SHORT(1),
2380 chidp = silc_public_key_payload_encode(pubkey);
2381 args = silc_argument_payload_encode_one(args,
2382 silc_buffer_datalen(chidp), 2);
2383 silc_buffer_free(chidp);
2384 silc_pkcs_public_key_free(pubkey);
2386 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2390 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2392 /* Send the command */
2393 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2394 1, silc_buffer_datalen(chidp),
2395 2, args ? action : NULL, args ? 1 : 0,
2396 3, silc_buffer_datalen(args));
2398 silc_buffer_free(chidp);
2399 silc_buffer_free(args);
2400 silc_client_unref_channel(client, conn, channel);
2402 /* Notify application */
2403 COMMAND(SILC_STATUS_OK);
2405 /** Wait for command reply */
2406 silc_fsm_next(fsm, silc_client_command_reply_wait);
2407 return SILC_FSM_CONTINUE;
2410 return SILC_FSM_FINISH;
2413 /********************************* DETACH ***********************************/
2415 /* Command DETACH. This is used to detach from the server */
2417 SILC_FSM_STATE(silc_client_command_detach)
2419 SilcClientCommandContext cmd = fsm_context;
2420 SilcClientConnection conn = cmd->conn;
2422 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2424 /* Notify application */
2425 COMMAND(SILC_STATUS_OK);
2427 /** Wait for command reply */
2428 silc_fsm_next(fsm, silc_client_command_reply_wait);
2429 return SILC_FSM_CONTINUE;
2432 /********************************** WATCH ***********************************/
2434 /* Command WATCH. */
2436 SILC_FSM_STATE(silc_client_command_watch)
2438 SilcClientCommandContext cmd = fsm_context;
2439 SilcClientConnection conn = cmd->conn;
2440 SilcBuffer args = NULL;
2442 const char *pubkey = NULL;
2443 SilcBool pubkey_add = TRUE;
2445 if (cmd->argc < 3) {
2446 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2450 if (!strcasecmp(cmd->argv[1], "-add")) {
2452 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2454 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2456 pubkey = cmd->argv[2] + 1;
2457 if (cmd->argv[2][0] == '-')
2460 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2468 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2469 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2470 "Could not load public key %s, check the filename", pubkey);
2471 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2475 args = silc_buffer_alloc_size(2);
2476 silc_buffer_format(args,
2477 SILC_STR_UI_SHORT(1),
2479 buffer = silc_public_key_payload_encode(pk);
2480 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2481 pubkey_add ? 0x00 : 0x01);
2482 silc_buffer_free(buffer);
2483 silc_pkcs_public_key_free(pk);
2486 /* If watching by nickname, resolve all users with that nickname so that
2487 we get their information immediately. */
2489 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2490 silc_client_command_resolve_dummy, NULL);
2492 /* Send the commmand */
2493 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2494 1, silc_buffer_datalen(conn->internal->
2496 type, pubkey ? args->data : cmd->argv[2],
2497 pubkey ? silc_buffer_len(args) :
2500 silc_buffer_free(args);
2502 /* Notify application */
2503 COMMAND(SILC_STATUS_OK);
2505 /** Wait for command reply */
2506 silc_fsm_next(fsm, silc_client_command_reply_wait);
2507 return SILC_FSM_CONTINUE;
2510 return SILC_FSM_FINISH;
2513 /********************************** LEAVE ***********************************/
2515 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2517 SILC_FSM_STATE(silc_client_command_leave)
2519 SilcClientCommandContext cmd = fsm_context;
2520 SilcClientConnection conn = cmd->conn;
2521 SilcClient client = conn->client;
2522 SilcChannelEntry channel;
2526 if (cmd->argc != 2) {
2527 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2528 "Usage: /LEAVE <channel>");
2529 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2533 if (cmd->argv[1][0] == '*') {
2534 if (!conn->current_channel) {
2535 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2538 name = conn->current_channel->channel_name;
2540 name = cmd->argv[1];
2543 /* Get the channel entry */
2544 channel = silc_client_get_channel(conn->client, conn, name);
2546 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2550 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2552 /* Send LEAVE command to the server */
2553 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2554 1, silc_buffer_datalen(idp));
2556 silc_buffer_free(idp);
2558 /* Notify application */
2559 COMMAND(SILC_STATUS_OK);
2561 if (conn->current_channel == channel)
2562 conn->current_channel = NULL;
2564 silc_client_unref_channel(client, conn, channel);
2566 /** Wait for command reply */
2567 silc_fsm_next(fsm, silc_client_command_reply_wait);
2568 return SILC_FSM_CONTINUE;
2571 return SILC_FSM_FINISH;
2574 /********************************** USERS ***********************************/
2576 /* Command USERS. Requests the USERS of the clients joined on requested
2579 SILC_FSM_STATE(silc_client_command_users)
2581 SilcClientCommandContext cmd = fsm_context;
2582 SilcClientConnection conn = cmd->conn;
2585 if (cmd->argc != 2) {
2586 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2587 "Usage: /USERS <channel>");
2588 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2592 if (cmd->argv[1][0] == '*') {
2593 if (!conn->current_channel) {
2594 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2597 name = conn->current_channel->channel_name;
2599 name = cmd->argv[1];
2602 /* Send USERS command to the server */
2603 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2604 2, name, strlen(name));
2606 /* Notify application */
2607 COMMAND(SILC_STATUS_OK);
2609 /** Wait for command reply */
2610 silc_fsm_next(fsm, silc_client_command_reply_wait);
2611 return SILC_FSM_CONTINUE;
2614 return SILC_FSM_FINISH;
2617 /********************************* GETKEY ***********************************/
2619 /* Command GETKEY. Used to fetch remote client's public key. */
2621 SILC_FSM_STATE(silc_client_command_getkey)
2623 SilcClientCommandContext cmd = fsm_context;
2624 SilcClientConnection conn = cmd->conn;
2625 SilcClient client = conn->client;
2626 SilcClientEntry client_entry;
2627 SilcServerEntry server_entry;
2631 if (cmd->argc < 2) {
2632 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2633 "Usage: /GETKEY <nickname or server name>");
2634 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2635 return SILC_FSM_FINISH;
2638 /* Find client entry */
2639 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2641 /* Check whether user requested server */
2642 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2643 if (!server_entry) {
2644 if (cmd->resolved) {
2645 /* Resolving didn't find anything. We should never get here as
2646 errors are handled in the resolving callback. */
2647 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2648 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2649 return SILC_FSM_FINISH;
2652 /* No client or server exist with this name, query for both. */
2653 cmd->resolved = TRUE;
2654 SILC_FSM_CALL(silc_client_command_send(client, conn,
2655 SILC_COMMAND_IDENTIFY,
2656 silc_client_command_continue,
2659 strlen(cmd->argv[1]),
2661 strlen(cmd->argv[1])));
2664 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2665 silc_client_unref_server(client, conn, server_entry);
2667 client_entry = silc_dlist_get(clients);
2668 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2669 silc_client_list_free(client, conn, clients);
2672 /* Send the commmand */
2673 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2674 1, silc_buffer_datalen(idp));
2676 silc_buffer_free(idp);
2678 /* Notify application */
2679 COMMAND(SILC_STATUS_OK);
2681 /** Wait for command reply */
2682 silc_fsm_next(fsm, silc_client_command_reply_wait);
2683 return SILC_FSM_CONTINUE;
2686 /********************************* SERVICE **********************************/
2688 /* Command SERVICE. Negotiates service agreement with server. */
2689 /* XXX incomplete */
2691 SILC_FSM_STATE(silc_client_command_service)
2693 SilcClientCommandContext cmd = fsm_context;
2695 SilcClientConnection conn = cmd->conn;
2699 if (cmd->argc < 2) {
2700 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2701 "Usage: /SERVICE [<service name>] [-pubkey]");
2702 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2703 return SILC_FSM_FINISH;
2706 name = cmd->argv[1];
2708 /* Send SERVICE command to the server */
2709 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2710 ++conn->cmd_ident, 1,
2711 1, name, strlen(name));
2712 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2713 NULL, 0, NULL, NULL, buffer->data,
2715 silc_buffer_free(buffer);
2718 /* Notify application */
2719 COMMAND(SILC_STATUS_OK);
2721 /** Wait for command reply */
2722 silc_fsm_next(fsm, silc_client_command_reply_wait);
2723 return SILC_FSM_CONTINUE;
2726 /* Register all default commands provided by the client library for the
2729 void silc_client_commands_register(SilcClient client)
2731 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2734 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2735 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2736 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2737 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2738 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2739 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2740 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2741 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2742 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2743 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2744 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2745 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2746 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2747 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2748 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2749 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2750 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2751 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2752 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2753 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2754 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2755 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2756 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2757 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2758 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2759 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2760 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2763 /* Unregister all commands. */
2765 void silc_client_commands_unregister(SilcClient client)
2767 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2768 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2769 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2770 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2771 SILC_CLIENT_CMDU(list, LIST, "LIST");
2772 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2773 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2774 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2775 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2776 SILC_CLIENT_CMDU(info, INFO, "INFO");
2777 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2778 SILC_CLIENT_CMDU(ping, PING, "PING");
2779 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2780 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2781 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2782 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2783 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2784 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2785 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2786 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2787 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2788 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2789 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2790 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2791 SILC_CLIENT_CMDU(users, USERS, "USERS");
2792 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2793 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2796 /****************** Client Side Incoming Command Handling *******************/
2798 /* Reply to WHOIS command from server */
2800 static void silc_client_command_process_whois(SilcClient client,
2801 SilcClientConnection conn,
2802 SilcCommandPayload payload,
2803 SilcArgumentPayload args)
2808 SilcBuffer buffer, packet;
2810 SILC_LOG_DEBUG(("Received WHOIS command"));
2812 /* Try to take the Requested Attributes */
2813 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2817 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2821 /* Process requested attributes */
2822 buffer = silc_client_attributes_process(client, conn, attrs);
2824 silc_attribute_payload_list_free(attrs);
2828 /* Send the attributes back in COMMAND_REPLY packet */
2830 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2832 silc_command_get_ident(payload),
2833 1, 11, buffer->data,
2834 silc_buffer_len(buffer));
2836 silc_buffer_free(buffer);
2840 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
2842 silc_packet_send(conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
2843 silc_buffer_datalen(packet));
2845 silc_buffer_free(packet);
2846 silc_buffer_free(buffer);
2849 /* Client is able to receive some command packets even though they are
2850 special case. Server may send WHOIS command to the client to retrieve
2851 Requested Attributes information for WHOIS query the server is
2852 processing. This function currently handles only the WHOIS command,
2853 but if in the future more commands may arrive then this can be made
2854 to support other commands too. */
2856 SILC_FSM_STATE(silc_client_command)
2858 SilcClientConnection conn = fsm_context;
2859 SilcClient client = conn->client;
2860 SilcPacket packet = state_context;
2861 SilcCommandPayload payload;
2862 SilcCommand command;
2863 SilcArgumentPayload args;
2865 /* Get command payload from packet */
2866 payload = silc_command_payload_parse(packet->buffer.data,
2867 silc_buffer_len(&packet->buffer));
2869 SILC_LOG_DEBUG(("Bad command packet"));
2870 return SILC_FSM_FINISH;
2874 args = silc_command_get_args(payload);
2876 /* Get the command */
2877 command = silc_command_get(payload);
2880 case SILC_COMMAND_WHOIS:
2881 /* Ignore everything if requested by application */
2882 if (conn->internal->params.ignore_requested_attributes)
2885 silc_client_command_process_whois(client, conn, payload, args);
2892 silc_command_payload_free(payload);
2893 return SILC_FSM_FINISH;