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 SilcUInt16 cmd_ident;
415 SilcClientCommand command;
416 SilcClientCommandContext cmd;
420 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
421 "You are not connected to a server, please connect to server");
425 /* Parse arguments */
426 va_start(va, command_line);
430 /* Get command name */
431 command_name = silc_memdup(command_line, strcspn(command_line, " "));
435 /* Find command by name */
436 command = silc_client_command_find(client, command_name);
438 silc_free(command_name);
442 /* Parse command line */
443 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
444 &argv_types, &argc, command->max_args);
446 silc_free(command_name);
448 arg = va_arg(va, char *);
452 /* Find command by name */
453 command = silc_client_command_find(client, arg);
458 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
459 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
460 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
461 if (!argv || !argv_lens || !argv_types)
463 argv[argc] = silc_memdup(arg, strlen(arg));
466 argv_lens[argc] = strlen(arg);
467 argv_types[argc] = argc;
469 arg = va_arg(va, char *);
474 /* Allocate command context */
475 cmd = silc_calloc(1, sizeof(*cmd));
479 cmd->cmd = command->cmd;
482 cmd->argv_lens = argv_lens;
483 cmd->argv_types = argv_types;
484 cmd_ident = cmd->cmd_ident = silc_client_cmd_ident(conn);
487 silc_list_init(cmd->reply_callbacks,
488 struct SilcClientCommandReplyCallbackStruct, next);
491 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
492 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
493 silc_client_command_destructor, NULL, FALSE);
494 silc_fsm_start_sync(&cmd->thread, command->command);
499 /* Generic function to send any command. The arguments must be sent already
500 encoded into correct format and in correct order. */
502 SilcUInt16 silc_client_command_send(SilcClient client,
503 SilcClientConnection conn,
505 SilcClientCommandReply reply,
507 SilcUInt32 argc, ...)
509 SilcClientCommandContext cmd;
515 /* Allocate command context */
516 cmd = silc_calloc(1, sizeof(*cmd));
521 silc_list_init(cmd->reply_callbacks,
522 struct SilcClientCommandReplyCallbackStruct, next);
524 /* Send the command */
527 silc_client_command_send_vap(client, conn, cmd, command, reply,
528 reply_context, argc, ap);
531 if (!cmd->cmd_ident) {
532 silc_client_command_free(cmd);
536 /*** Wait for command reply */
537 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
538 silc_client_command_destructor, NULL, FALSE);
539 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
541 return cmd->cmd_ident;
544 /* Generic function to send any command. The arguments must be sent already
545 encoded into correct format and in correct order. Arguments come from
548 SilcUInt16 silc_client_command_send_argv(SilcClient client,
549 SilcClientConnection conn,
551 SilcClientCommandReply reply,
554 unsigned char **argv,
555 SilcUInt32 *argv_lens,
556 SilcUInt32 *argv_types)
558 SilcClientCommandContext cmd;
563 /* Allocate command context */
564 cmd = silc_calloc(1, sizeof(*cmd));
570 /* Send the command */
572 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
573 reply_context, argc, argv, argv_lens,
575 if (!cmd->cmd_ident) {
576 silc_client_command_free(cmd);
580 /*** Wait for command reply */
581 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
582 silc_client_command_destructor, NULL, FALSE);
583 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
585 return cmd->cmd_ident;
588 /* Attach to a command and command identifier to receive command reply. */
590 SilcBool silc_client_command_pending(SilcClientConnection conn,
593 SilcClientCommandReply reply,
596 SilcClientCommandContext cmd;
597 SilcClientCommandReplyCallback cb;
602 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
604 silc_mutex_lock(conn->internal->lock);
606 /* Find the pending command */
607 silc_list_start(conn->internal->pending_commands);
608 while ((cmd = silc_list_get(conn->internal->pending_commands)))
609 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
610 && cmd->cmd_ident == ident) {
612 /* Add the callback */
613 cb = silc_calloc(1, sizeof(*cb));
617 cb->context = context;
618 silc_list_add(cmd->reply_callbacks, cb);
621 silc_mutex_unlock(conn->internal->lock);
626 /******************************** WHOIS *************************************/
628 /* Command WHOIS. This command is used to query information about
631 SILC_FSM_STATE(silc_client_command_whois)
633 SilcClientCommandContext cmd = fsm_context;
634 SilcClientConnection conn = cmd->conn;
635 SilcClient client = conn->client;
636 SilcBuffer attrs = NULL;
637 unsigned char count[4], *tmp = NULL;
638 SilcBool details = FALSE, nick = FALSE;
639 unsigned char *pubkey = NULL;
640 char *nickname = NULL;
643 /* Given without arguments fetches client's own information */
645 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
646 silc_buffer_data(conn->internal->local_idp),
647 silc_buffer_len(conn->internal->local_idp));
649 /* Notify application */
650 COMMAND(SILC_STATUS_OK);
652 /** Wait for command reply */
653 silc_fsm_next(fsm, silc_client_command_reply_wait);
654 return SILC_FSM_CONTINUE;
657 for (i = 1; i < cmd->argc; i++) {
658 if (!strcasecmp(cmd->argv[i], "-details")) {
660 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
661 pubkey = cmd->argv[i + 1];
664 /* We assume that the first parameter is the nickname, if it isn't
665 -details or -pubkey. The last parameter should always be the count */
668 } else if (i == cmd->argc - 1) {
669 int c = atoi(cmd->argv[i]);
670 SILC_PUT32_MSB(c, count);
677 /* If pubkey is set, add all attributes to the attrs buffer, except
680 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
681 SILC_ATTRIBUTE_SERVICE,
682 SILC_ATTRIBUTE_STATUS_MOOD,
683 SILC_ATTRIBUTE_STATUS_FREETEXT,
684 SILC_ATTRIBUTE_STATUS_MESSAGE,
685 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
686 SILC_ATTRIBUTE_PREFERRED_CONTACT,
687 SILC_ATTRIBUTE_TIMEZONE,
688 SILC_ATTRIBUTE_GEOLOCATION,
689 SILC_ATTRIBUTE_DEVICE_INFO,
690 SILC_ATTRIBUTE_USER_ICON, 0);
692 attrs = silc_client_attributes_request(0);
697 SilcAttributeObjPk obj;
700 if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
701 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
702 "Could not load public key %s, check the filename",
704 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
708 switch (silc_pkcs_get_type(pk)) {
710 obj.type = "silc-rsa";
713 obj.type = "ssh-rsa";
715 case SILC_PKCS_X509V3:
716 obj.type = "x509v3-sign-rsa";
718 case SILC_PKCS_OPENPGP:
719 obj.type = "pgp-sign-rsa";
725 obj.data = silc_pkcs_public_key_encode(NULL, pk, &obj.data_len);
727 attrs = silc_attribute_payload_encode(attrs,
728 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
729 SILC_ATTRIBUTE_FLAG_VALID,
735 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
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;
941 char *name, tmp[512];
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);
957 if (client->internal->params->full_channel_names)
958 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
960 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
961 conn->current_channel->channel_name,
962 conn->current_channel->server[0] ? "@" : "",
963 conn->current_channel->server);
969 if (!conn->current_channel) {
970 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
974 /* Get the Channel ID of the channel */
975 channel = silc_client_get_channel(conn->client, conn, name);
977 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
981 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
983 /* Send TOPIC command to the server */
985 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
986 1, silc_buffer_datalen(idp),
987 2, cmd->argv[2], strlen(cmd->argv[2]));
989 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
990 1, silc_buffer_datalen(idp));
992 silc_buffer_free(idp);
993 silc_client_unref_channel(client, conn, channel);
995 /* Notify application */
996 COMMAND(SILC_STATUS_OK);
998 /** Wait for command reply */
999 silc_fsm_next(fsm, silc_client_command_reply_wait);
1000 return SILC_FSM_CONTINUE;
1003 return SILC_FSM_FINISH;
1006 /********************************* INVITE ***********************************/
1008 /* Command INVITE. Invites specific client to join a channel. This is
1009 also used to mange the invite list of the channel. */
1011 SILC_FSM_STATE(silc_client_command_invite)
1013 SilcClientCommandContext cmd = fsm_context;
1014 SilcClientConnection conn = cmd->conn;
1015 SilcClient client = conn->client;
1016 SilcClientEntry client_entry = NULL;
1017 SilcChannelEntry channel = NULL;
1018 SilcBuffer clidp, chidp, args = NULL;
1019 SilcPublicKey pubkey = NULL;
1020 SilcDList clients = NULL;
1021 char *nickname = NULL, *name;
1022 char *invite = NULL;
1023 unsigned char action[1];
1025 if (cmd->argc < 2) {
1026 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1027 "Usage: /INVITE <channel> [<nickname>[@server>]"
1028 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1029 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1033 if (cmd->argv[1][0] == '*') {
1034 if (!conn->current_channel) {
1035 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1039 channel = conn->current_channel;
1040 silc_client_ref_channel(client, conn, channel);
1042 name = cmd->argv[1];
1044 channel = silc_client_get_channel(conn->client, conn, name);
1046 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1051 /* Parse the typed nickname. */
1052 if (cmd->argc == 3) {
1053 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1054 if (!silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname)) {
1055 silc_client_unref_channel(client, conn, channel);
1059 /* Find client entry */
1060 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1063 /* Resolve client information */
1064 SILC_FSM_CALL(silc_client_get_clients(
1065 client, conn, nickname, NULL,
1066 silc_client_command_resolve_continue,
1069 client_entry = silc_dlist_get(clients);
1071 if (cmd->argv[2][0] == '+')
1076 /* Check if it is public key file to be added to invite list */
1077 silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
1078 invite = cmd->argv[2];
1085 args = silc_buffer_alloc_size(2);
1086 silc_buffer_format(args,
1087 SILC_STR_UI_SHORT(1),
1090 chidp = silc_public_key_payload_encode(NULL, pubkey);
1091 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1092 silc_buffer_len(chidp), 2);
1093 silc_buffer_free(chidp);
1094 silc_pkcs_public_key_free(pubkey);
1096 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1100 /* Send the command */
1101 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1103 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1104 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1105 1, silc_buffer_datalen(chidp),
1106 2, silc_buffer_datalen(clidp),
1107 3, args ? action : NULL, args ? 1 : 0,
1108 4, silc_buffer_datalen(args));
1109 silc_buffer_free(clidp);
1111 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1112 1, silc_buffer_datalen(chidp),
1113 3, args ? action : NULL, args ? 1 : 0,
1114 4, silc_buffer_datalen(args));
1117 silc_buffer_free(chidp);
1118 silc_buffer_free(args);
1119 silc_free(nickname);
1120 silc_client_list_free(client, conn, clients);
1121 silc_client_unref_channel(client, conn, channel);
1123 /* Notify application */
1124 COMMAND(SILC_STATUS_OK);
1126 /** Wait for command reply */
1127 silc_fsm_next(fsm, silc_client_command_reply_wait);
1128 return SILC_FSM_CONTINUE;
1131 silc_free(nickname);
1132 return SILC_FSM_FINISH;
1135 /********************************** QUIT ************************************/
1137 /* Close the connection */
1139 SILC_FSM_STATE(silc_client_command_quit_final)
1141 SilcClientCommandContext cmd = fsm_context;
1142 SilcClientConnection conn = cmd->conn;
1144 SILC_LOG_DEBUG(("Quitting"));
1146 /* Notify application */
1147 COMMAND(SILC_STATUS_OK);
1149 /* Signal to close connection */
1150 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1151 if (!conn->internal->disconnected) {
1152 conn->internal->disconnected = TRUE;
1153 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1156 return SILC_FSM_FINISH;
1159 /* Command QUIT. Closes connection with current server. */
1161 SILC_FSM_STATE(silc_client_command_quit)
1163 SilcClientCommandContext cmd = fsm_context;
1164 SilcClientConnection conn = cmd->conn;
1167 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1168 1, cmd->argv[1], cmd->argv_lens[1]);
1170 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1172 /* Sleep for a while */
1175 /* We close the connection with a little timeout */
1176 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1177 return SILC_FSM_WAIT;
1180 /********************************** KILL ************************************/
1182 /* Signature callback */
1184 static void silc_client_command_kill_signed(const SilcBuffer buffer,
1187 SilcClientCommandContext cmd = context;
1190 silc_fsm_finish(&cmd->thread);
1194 silc_fsm_set_state_context(&cmd->thread, buffer);
1195 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1198 /* Send KILL command */
1200 SILC_FSM_STATE(silc_client_command_kill_send)
1202 SilcClientCommandContext cmd = fsm_context;
1203 SilcClientConnection conn = cmd->conn;
1204 SilcClient client = conn->client;
1205 SilcBuffer idp, auth = state_context;
1206 SilcClientEntry target = cmd->context;
1207 char *comment = NULL;
1210 if (strcasecmp(cmd->argv[2], "-pubkey"))
1211 comment = cmd->argv[2];
1213 /* Send the KILL command to the server */
1214 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1215 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1216 1, silc_buffer_datalen(idp),
1217 2, comment, comment ? strlen(comment) : 0,
1218 3, silc_buffer_datalen(auth));
1220 silc_buffer_free(idp);
1221 silc_client_unref_client(client, conn, target);
1223 /* Notify application */
1224 COMMAND(SILC_STATUS_OK);
1226 /** Wait for command reply */
1227 silc_fsm_next(fsm, silc_client_command_reply_wait);
1228 return SILC_FSM_CONTINUE;
1231 /* Command KILL. Router operator can use this command to remove an client
1232 fromthe SILC Network. */
1234 SILC_FSM_STATE(silc_client_command_kill)
1236 SilcClientCommandContext cmd = fsm_context;
1237 SilcClientConnection conn = cmd->conn;
1238 SilcClient client = conn->client;
1239 SilcClientEntry target;
1241 char *nickname = NULL;
1243 if (cmd->argc < 2) {
1244 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1245 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1246 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1247 return SILC_FSM_FINISH;
1250 /* Parse the typed nickname. */
1251 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1252 return SILC_FSM_FINISH;
1254 /* Get the target client */
1255 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1257 /* Resolve client information */
1258 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1259 silc_client_command_resolve_continue,
1262 target = silc_dlist_get(clients);
1263 cmd->context = silc_client_ref_client(client, conn, target);
1265 silc_free(nickname);
1266 silc_client_list_free(client, conn, clients);
1269 silc_fsm_next(fsm, silc_client_command_kill_send);
1271 if (cmd->argc >= 3) {
1272 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1273 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1274 /* Encode the public key authentication payload */
1275 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1279 conn->internal->sha1hash,
1280 &target->id, SILC_ID_CLIENT,
1281 silc_client_command_kill_signed,
1287 return SILC_FSM_CONTINUE;
1290 /********************************** INFO ************************************/
1292 /* Command INFO. Request information about specific server. If specific
1293 server is not provided the current server is used. */
1295 SILC_FSM_STATE(silc_client_command_info)
1297 SilcClientCommandContext cmd = fsm_context;
1298 SilcClientConnection conn = cmd->conn;
1300 /* Send the command */
1302 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1303 1, cmd->argv[1], cmd->argv_lens[1]);
1305 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1307 /* Notify application */
1308 COMMAND(SILC_STATUS_OK);
1310 /** Wait for command reply */
1311 silc_fsm_next(fsm, silc_client_command_reply_wait);
1312 return SILC_FSM_CONTINUE;
1315 /********************************** STATS ***********************************/
1317 /* Command STATS. Shows server and network statistics. */
1319 SILC_FSM_STATE(silc_client_command_stats)
1321 SilcClientCommandContext cmd = fsm_context;
1322 SilcClientConnection conn = cmd->conn;
1324 /* Send the command */
1325 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1326 1, silc_buffer_datalen(conn->internal->
1329 /* Notify application */
1330 COMMAND(SILC_STATUS_OK);
1332 /** Wait for command reply */
1333 silc_fsm_next(fsm, silc_client_command_reply_wait);
1334 return SILC_FSM_CONTINUE;
1337 /********************************** PING ************************************/
1339 /* Command PING. Sends ping to server. */
1341 SILC_FSM_STATE(silc_client_command_ping)
1343 SilcClientCommandContext cmd = fsm_context;
1344 SilcClientConnection conn = cmd->conn;
1346 if (cmd->argc < 2) {
1347 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1348 return SILC_FSM_FINISH;
1351 /* Send the command */
1352 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1353 1, silc_buffer_datalen(conn->internal->
1356 /* Save ping time */
1357 cmd->context = SILC_64_TO_PTR(silc_time());
1359 /* Notify application */
1360 COMMAND(SILC_STATUS_OK);
1362 /** Wait for command reply */
1363 silc_fsm_next(fsm, silc_client_command_reply_wait);
1364 return SILC_FSM_CONTINUE;
1367 /********************************** JOIN ************************************/
1373 } *SilcClientJoinContext;
1375 /* Signature callback */
1377 static void silc_client_command_join_signed(const SilcBuffer buffer,
1380 SilcClientCommandContext cmd = context;
1381 SilcClientJoinContext j = cmd->context;
1384 silc_fsm_finish(&cmd->thread);
1389 j->auth = silc_buffer_copy(buffer);
1391 j->cauth = silc_buffer_copy(buffer);
1393 SILC_FSM_CALL_CONTINUE(&cmd->thread);
1396 /* Command JOIN. Joins to a channel. */
1398 SILC_FSM_STATE(silc_client_command_join)
1400 SilcClientCommandContext cmd2, cmd = fsm_context;
1401 SilcClientConnection conn = cmd->conn;
1402 SilcClient client = conn->client;
1403 SilcChannelEntry channel = NULL;
1404 SilcClientJoinContext j = cmd->context;
1405 SilcBuffer auth = NULL, cauth = NULL;
1406 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1407 int i, passphrase_len = 0;
1409 if (cmd->argc < 2) {
1410 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1414 /* See if we have joined to the requested channel already */
1415 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1416 if (channel && silc_client_on_channel(channel, conn->local_entry))
1419 /* If NICK command is active, wait for it to finish before sending JOIN.
1420 To avoid problems locally with changing IDs while joining, we do this. */
1421 silc_mutex_lock(conn->internal->lock);
1422 silc_list_start(conn->internal->pending_commands);
1423 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1424 if (cmd2->cmd == SILC_COMMAND_NICK) {
1425 silc_mutex_unlock(conn->internal->lock);
1426 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1427 return SILC_FSM_WAIT;
1430 silc_mutex_unlock(conn->internal->lock);
1432 if (cmd->argv_lens[1] > 256)
1433 cmd->argv_lens[1] = 256;
1435 name = cmd->argv[1];
1437 for (i = 2; i < cmd->argc; i++) {
1438 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1439 cipher = cmd->argv[++i];
1440 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1441 hmac = cmd->argv[++i];
1442 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1443 if (!j || !j->auth) {
1445 j = silc_calloc(1, sizeof(*j));
1451 silc_free(passphrase);
1452 silc_client_unref_channel(client, conn, channel);
1453 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1457 conn->internal->sha1hash,
1460 silc_client_command_join_signed,
1464 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1465 SilcPublicKey pubkey = conn->public_key;
1466 SilcPrivateKey privkey = conn->private_key;
1467 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], pubdata[128];
1470 if (!j || !j->cauth) {
1472 j = silc_calloc(1, sizeof(*j));
1478 if (cmd->argc >= i + 3) {
1480 if (cmd->argc >= i + 4) {
1481 pass = cmd->argv[i + 3];
1484 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1485 &pubkey, &privkey)) {
1486 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1487 "Could not load key pair, check your arguments");
1488 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1495 pk = silc_pkcs_public_key_encode(NULL, pubkey, &pk_len);
1496 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1498 silc_rng_get_rn_data(conn->client->rng, sizeof(pubdata), pubdata,
1500 memcpy(pubdata, pkhash, 20);
1501 silc_free(passphrase);
1502 silc_client_unref_channel(client, conn, channel);
1503 SILC_FSM_CALL(silc_auth_public_key_auth_generate_wpub(
1505 pubdata, sizeof(pubdata),
1506 conn->internal->sha1hash,
1510 silc_client_command_join_signed,
1515 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1516 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1517 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1518 cmd->argv_lens[i], 0);
1519 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1520 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1521 0, pu8, passphrase_len);
1524 passphrase = strdup(cmd->argv[i]);
1525 passphrase_len = cmd->argv_lens[i];
1530 /* Send JOIN command to the server */
1531 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1532 1, name, strlen(name),
1533 2, silc_buffer_datalen(conn->internal->
1535 3, passphrase, passphrase_len,
1536 4, cipher, cipher ? strlen(cipher) : 0,
1537 5, hmac, hmac ? strlen(hmac) : 0,
1538 6, silc_buffer_datalen(auth),
1539 7, silc_buffer_datalen(cauth));
1542 memset(passphrase, 0, strlen(passphrase));
1543 silc_free(passphrase);
1544 silc_client_unref_channel(client, conn, channel);
1546 silc_buffer_free(j->auth);
1547 silc_buffer_free(j->cauth);
1551 /* Notify application */
1552 COMMAND(SILC_STATUS_OK);
1554 /** Wait for command reply */
1555 silc_fsm_next(fsm, silc_client_command_reply_wait);
1556 return SILC_FSM_CONTINUE;
1559 silc_client_unref_channel(client, conn, channel);
1560 return SILC_FSM_FINISH;
1563 /********************************** MOTD ************************************/
1565 /* MOTD command. Requests motd from server. */
1567 SILC_FSM_STATE(silc_client_command_motd)
1569 SilcClientCommandContext cmd = fsm_context;
1570 SilcClientConnection conn = cmd->conn;
1572 if (cmd->argc < 1 || cmd->argc > 2) {
1573 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1574 "Usage: /MOTD [<server>]");
1575 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1576 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1577 return SILC_FSM_FINISH;
1580 /* Send the command */
1582 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1583 1, conn->remote_host,
1584 strlen(conn->remote_host));
1586 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1587 1, cmd->argv[1], cmd->argv_lens[1]);
1589 /* Notify application */
1590 COMMAND(SILC_STATUS_OK);
1592 /** Wait for command reply */
1593 silc_fsm_next(fsm, silc_client_command_reply_wait);
1594 return SILC_FSM_CONTINUE;
1597 /********************************** UMODE ***********************************/
1599 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1600 modes as client cannot set itself server/router operator privileges. */
1602 SILC_FSM_STATE(silc_client_command_umode)
1604 SilcClientCommandContext cmd = fsm_context;
1605 SilcClientConnection conn = cmd->conn;
1606 unsigned char *cp, modebuf[4];
1607 SilcUInt32 mode, add, len;
1610 if (cmd->argc < 2) {
1611 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1612 "Usage: /UMODE +|-<modes>");
1613 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1614 return SILC_FSM_FINISH;
1617 mode = conn->local_entry->mode;
1619 /* Are we adding or removing mode */
1620 if (cmd->argv[1][0] == '-')
1626 cp = cmd->argv[1] + 1;
1628 for (i = 0; i < len; i++) {
1633 mode |= SILC_UMODE_SERVER_OPERATOR;
1634 mode |= SILC_UMODE_ROUTER_OPERATOR;
1635 mode |= SILC_UMODE_GONE;
1636 mode |= SILC_UMODE_INDISPOSED;
1637 mode |= SILC_UMODE_BUSY;
1638 mode |= SILC_UMODE_PAGE;
1639 mode |= SILC_UMODE_HYPER;
1640 mode |= SILC_UMODE_ROBOT;
1641 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1642 mode |= SILC_UMODE_REJECT_WATCHING;
1644 mode = SILC_UMODE_NONE;
1649 mode |= SILC_UMODE_SERVER_OPERATOR;
1651 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1655 mode |= SILC_UMODE_ROUTER_OPERATOR;
1657 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1661 mode |= SILC_UMODE_GONE;
1663 mode &= ~SILC_UMODE_GONE;
1667 mode |= SILC_UMODE_INDISPOSED;
1669 mode &= ~SILC_UMODE_INDISPOSED;
1673 mode |= SILC_UMODE_BUSY;
1675 mode &= ~SILC_UMODE_BUSY;
1679 mode |= SILC_UMODE_PAGE;
1681 mode &= ~SILC_UMODE_PAGE;
1685 mode |= SILC_UMODE_HYPER;
1687 mode &= ~SILC_UMODE_HYPER;
1691 mode |= SILC_UMODE_ROBOT;
1693 mode &= ~SILC_UMODE_ROBOT;
1697 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1699 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1703 mode |= SILC_UMODE_REJECT_WATCHING;
1705 mode &= ~SILC_UMODE_REJECT_WATCHING;
1709 mode |= SILC_UMODE_BLOCK_INVITE;
1711 mode &= ~SILC_UMODE_BLOCK_INVITE;
1714 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1715 return SILC_FSM_FINISH;
1720 SILC_PUT32_MSB(mode, modebuf);
1722 /* Send the command */
1723 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1724 1, silc_buffer_datalen(conn->internal->
1726 2, modebuf, sizeof(modebuf));
1728 /* Notify application */
1729 COMMAND(SILC_STATUS_OK);
1731 /** Wait for command reply */
1732 silc_fsm_next(fsm, silc_client_command_reply_wait);
1733 return SILC_FSM_CONTINUE;
1736 /********************************** CMODE ***********************************/
1738 /* Signature callback */
1740 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1743 SilcClientCommandContext cmd = context;
1746 silc_fsm_finish(&cmd->thread);
1750 silc_fsm_set_state_context(&cmd->thread, buffer);
1751 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1754 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1755 can be set several at once. Those modes that require argument must be set
1756 separately (unless set with modes that does not require arguments). */
1758 SILC_FSM_STATE(silc_client_command_cmode)
1760 SilcClientCommandContext cmd = fsm_context;
1761 SilcClientConnection conn = cmd->conn;
1762 SilcClient client = conn->client;
1763 SilcBuffer auth = state_context;
1764 SilcChannelEntry channel = NULL;
1765 SilcBuffer chidp, pk = NULL;
1766 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1767 SilcUInt32 mode, add, type, len, arg_len = 0;
1770 if (cmd->argc < 3) {
1771 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1772 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1773 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1777 if (cmd->argv[1][0] == '*') {
1778 if (!conn->current_channel) {
1779 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1783 channel = conn->current_channel;
1784 silc_client_ref_channel(client, conn, channel);
1786 name = cmd->argv[1];
1788 channel = silc_client_get_channel(conn->client, conn, name);
1790 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1795 mode = channel->mode;
1797 /* Are we adding or removing mode */
1798 if (cmd->argv[2][0] == '-')
1803 /* Argument type to be sent to server */
1807 cp = cmd->argv[2] + 1;
1809 for (i = 0; i < len; i++) {
1813 mode |= SILC_CHANNEL_MODE_PRIVATE;
1815 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1819 mode |= SILC_CHANNEL_MODE_SECRET;
1821 mode &= ~SILC_CHANNEL_MODE_SECRET;
1825 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1827 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1831 mode |= SILC_CHANNEL_MODE_INVITE;
1833 mode &= ~SILC_CHANNEL_MODE_INVITE;
1837 mode |= SILC_CHANNEL_MODE_TOPIC;
1839 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1843 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1845 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1849 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1851 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1856 mode |= SILC_CHANNEL_MODE_ULIMIT;
1858 if (cmd->argc < 4) {
1859 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1860 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1861 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1864 ll = atoi(cmd->argv[3]);
1865 SILC_PUT32_MSB(ll, tmp);
1869 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1874 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1876 if (cmd->argc < 4) {
1877 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1878 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1879 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1883 arg_len = cmd->argv_lens[3];
1885 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1890 mode |= SILC_CHANNEL_MODE_CIPHER;
1892 if (cmd->argc < 4) {
1893 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1894 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1895 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1899 arg_len = cmd->argv_lens[3];
1901 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1906 mode |= SILC_CHANNEL_MODE_HMAC;
1908 if (cmd->argc < 4) {
1909 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1910 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1911 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1915 arg_len = cmd->argv_lens[3];
1917 mode &= ~SILC_CHANNEL_MODE_HMAC;
1923 SilcPublicKey pubkey = conn->public_key;
1924 SilcPrivateKey privkey = conn->private_key;
1926 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1929 if (cmd->argc >= 5) {
1932 pass = cmd->argv[5];
1933 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1934 &pubkey, &privkey)) {
1935 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1936 "Could not load key pair, check your arguments");
1937 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1942 pk = silc_public_key_payload_encode(NULL, pubkey);
1943 silc_client_unref_channel(client, conn, channel);
1944 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1947 conn->internal->sha1hash,
1950 silc_client_command_cmode_signed,
1954 arg = silc_buffer_data(auth);
1955 arg_len = silc_buffer_len(auth);
1957 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1963 SilcBool chadd = FALSE;
1964 SilcPublicKey chpk = NULL;
1966 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1969 if (cmd->argc == 3) {
1970 /* Send empty command to receive the public key list. */
1971 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1972 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1974 1, silc_buffer_datalen(chidp));
1975 silc_buffer_free(chidp);
1976 silc_client_unref_channel(client, conn, channel);
1978 /* Notify application */
1979 COMMAND(SILC_STATUS_OK);
1981 /** Wait for command reply */
1982 silc_fsm_next(fsm, silc_client_command_reply_wait);
1983 return SILC_FSM_CONTINUE;
1986 if (cmd->argc >= 4) {
1987 auth = silc_buffer_alloc_size(2);
1988 silc_buffer_format(auth,
1989 SILC_STR_UI_SHORT(cmd->argc - 3),
1993 for (k = 3; k < cmd->argc; k++) {
1994 if (cmd->argv[k][0] == '+')
1996 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, SILC_PKCS_ANY,
1998 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1999 "Could not load public key %s, check the filename",
2001 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2002 silc_buffer_free(auth);
2007 pk = silc_public_key_payload_encode(NULL, chpk);
2008 auth = silc_argument_payload_encode_one(auth,
2009 silc_buffer_datalen(pk),
2010 chadd ? 0x00 : 0x01);
2011 silc_pkcs_public_key_free(chpk);
2012 silc_buffer_free(pk);
2017 arg = silc_buffer_data(auth);
2018 arg_len = silc_buffer_len(auth);
2020 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2024 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2030 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2031 SILC_PUT32_MSB(mode, modebuf);
2033 /* Send the command. We support sending only one mode at once that
2034 requires an argument. */
2036 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2037 1, silc_buffer_datalen(chidp),
2038 2, modebuf, sizeof(modebuf),
2040 8, silc_buffer_datalen(pk));
2042 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2043 1, silc_buffer_datalen(chidp),
2044 2, modebuf, sizeof(modebuf));
2047 silc_buffer_free(chidp);
2048 silc_buffer_free(auth);
2049 silc_buffer_free(pk);
2050 silc_client_unref_channel(client, conn, channel);
2052 /* Notify application */
2053 COMMAND(SILC_STATUS_OK);
2055 /** Wait for command reply */
2056 silc_fsm_next(fsm, silc_client_command_reply_wait);
2057 return SILC_FSM_CONTINUE;
2060 silc_client_unref_channel(client, conn, channel);
2061 return SILC_FSM_FINISH;
2064 /********************************* CUMODE ***********************************/
2066 /* Signature callback */
2068 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2071 SilcClientCommandContext cmd = context;
2074 silc_fsm_finish(&cmd->thread);
2078 silc_fsm_set_state_context(&cmd->thread, buffer);
2079 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2082 /* CUMODE command. Changes client's mode on a channel. */
2084 SILC_FSM_STATE(silc_client_command_cumode)
2086 SilcClientCommandContext cmd = fsm_context;
2087 SilcClientConnection conn = cmd->conn;
2088 SilcClient client = conn->client;
2089 SilcBuffer auth = state_context;
2090 SilcChannelEntry channel = NULL;
2091 SilcChannelUser chu;
2092 SilcClientEntry client_entry;
2093 SilcBuffer clidp, chidp;
2094 SilcDList clients = NULL;
2095 unsigned char *name, *cp, modebuf[4];
2096 SilcUInt32 mode = 0, add, len;
2097 char *nickname = NULL;
2100 if (cmd->argc < 4) {
2101 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2102 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2103 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2107 if (cmd->argv[1][0] == '*') {
2108 if (!conn->current_channel) {
2109 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2113 channel = conn->current_channel;
2114 silc_client_ref_channel(client, conn, channel);
2116 name = cmd->argv[1];
2118 channel = silc_client_get_channel(conn->client, conn, name);
2120 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2125 /* Parse the typed nickname. */
2126 if (!silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname))
2129 /* Find client entry */
2130 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2132 /* Resolve client information */
2133 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2134 silc_client_command_resolve_continue,
2137 client_entry = silc_dlist_get(clients);
2139 /* Get the current mode */
2140 chu = silc_client_on_channel(channel, client_entry);
2144 /* Are we adding or removing mode */
2145 if (cmd->argv[2][0] == '-')
2151 cp = cmd->argv[2] + 1;
2153 for (i = 0; i < len; i++) {
2157 mode |= SILC_CHANNEL_UMODE_CHANFO;
2158 mode |= SILC_CHANNEL_UMODE_CHANOP;
2159 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2160 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2161 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2163 mode = SILC_CHANNEL_UMODE_NONE;
2169 SilcPublicKey pubkey = conn->public_key;
2170 SilcPrivateKey privkey = conn->private_key;
2172 if (cmd->argc >= 6) {
2175 pass = cmd->argv[6];
2176 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2177 &pubkey, &privkey)) {
2178 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2179 "Could not load key pair, check your arguments");
2180 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2185 silc_free(nickname);
2186 silc_client_list_free(client, conn, clients);
2187 silc_client_unref_channel(client, conn, channel);
2189 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2192 conn->internal->sha1hash,
2195 silc_client_command_cumode_signed,
2200 mode |= SILC_CHANNEL_UMODE_CHANFO;
2202 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2207 mode |= SILC_CHANNEL_UMODE_CHANOP;
2209 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2213 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2215 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2219 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2221 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2225 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2227 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2231 mode |= SILC_CHANNEL_UMODE_QUIET;
2233 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2236 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2242 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2243 SILC_PUT32_MSB(mode, modebuf);
2244 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2246 /* Send the command packet. We support sending only one mode at once
2247 that requires an argument. */
2248 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2249 1, silc_buffer_datalen(chidp),
2251 3, silc_buffer_datalen(clidp),
2252 4, silc_buffer_datalen(auth));
2254 silc_buffer_free(chidp);
2255 silc_buffer_free(clidp);
2257 silc_buffer_free(auth);
2258 silc_free(nickname);
2259 silc_client_list_free(client, conn, clients);
2260 silc_client_unref_channel(client, conn, channel);
2262 /* Notify application */
2263 COMMAND(SILC_STATUS_OK);
2265 /** Wait for command reply */
2266 silc_fsm_next(fsm, silc_client_command_reply_wait);
2267 return SILC_FSM_CONTINUE;
2270 silc_client_unref_channel(client, conn, channel);
2271 silc_client_list_free(client, conn, clients);
2272 silc_free(nickname);
2273 return SILC_FSM_FINISH;
2276 /********************************** KICK ************************************/
2278 /* KICK command. Kicks a client out of channel. */
2280 SILC_FSM_STATE(silc_client_command_kick)
2282 SilcClientCommandContext cmd = fsm_context;
2283 SilcClientConnection conn = cmd->conn;
2284 SilcClient client = conn->client;
2285 SilcChannelEntry channel = NULL;
2286 SilcBuffer idp, idp2;
2287 SilcClientEntry target;
2288 SilcDList clients = NULL;
2289 char *name, tmp[512];
2291 if (cmd->argc < 3) {
2292 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2293 "Usage: /KICK <channel> <nickname> [<comment>]");
2294 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2298 if (cmd->argv[1][0] == '*') {
2299 if (!conn->current_channel) {
2300 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2304 if (client->internal->params->full_channel_names)
2305 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2307 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2308 conn->current_channel->channel_name,
2309 conn->current_channel->server[0] ? "@" : "",
2310 conn->current_channel->server);
2313 name = cmd->argv[1];
2316 if (!conn->current_channel) {
2317 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2321 /* Get the Channel ID of the channel */
2322 channel = silc_client_get_channel(conn->client, conn, name);
2324 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2328 /* Get the target client */
2329 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2331 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2332 "No such client: %s", cmd->argv[2]);
2333 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2336 target = silc_dlist_get(clients);
2338 /* Send KICK command to the server */
2339 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2340 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2342 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2343 1, silc_buffer_datalen(idp),
2344 2, silc_buffer_datalen(idp2));
2346 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2347 1, silc_buffer_datalen(idp),
2348 2, silc_buffer_datalen(idp2),
2349 3, cmd->argv[3], strlen(cmd->argv[3]));
2351 silc_buffer_free(idp);
2352 silc_buffer_free(idp2);
2353 silc_client_list_free(client, conn, clients);
2354 silc_client_unref_channel(client, conn, channel);
2356 /* Notify application */
2357 COMMAND(SILC_STATUS_OK);
2359 /** Wait for command reply */
2360 silc_fsm_next(fsm, silc_client_command_reply_wait);
2361 return SILC_FSM_CONTINUE;
2364 silc_client_unref_channel(client, conn, channel);
2365 return SILC_FSM_FINISH;
2368 /***************************** OPER & SILCOPER ******************************/
2371 unsigned char *passphrase;
2372 SilcUInt32 passphrase_len;
2374 } *SilcClientCommandOper;
2376 /* Ask passphrase callback */
2378 static void silc_client_command_oper_cb(const unsigned char *data,
2379 SilcUInt32 data_len, void *context)
2381 SilcClientCommandContext cmd = context;
2382 SilcClientCommandOper oper = cmd->context;
2384 if (data && data_len)
2385 oper->passphrase = silc_memdup(data, data_len);
2386 oper->passphrase_len = data_len;
2389 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2392 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2395 SilcClientCommandContext cmd = context;
2396 SilcClientCommandOper oper = cmd->context;
2399 oper->auth = silc_buffer_copy(data);
2402 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2405 /* Send OPER/SILCOPER command */
2407 SILC_FSM_STATE(silc_client_command_oper_send)
2409 SilcClientCommandContext cmd = fsm_context;
2410 SilcClientConnection conn = cmd->conn;
2411 SilcClientCommandOper oper = cmd->context;
2412 SilcBuffer auth = oper ? oper->auth : NULL;
2414 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2415 1, cmd->argv[1], strlen(cmd->argv[1]),
2416 2, silc_buffer_datalen(auth));
2418 silc_buffer_clear(auth);
2419 silc_buffer_free(auth);
2421 silc_free(oper->passphrase);
2425 /* Notify application */
2426 COMMAND(SILC_STATUS_OK);
2428 /** Wait for command reply */
2429 silc_fsm_next(fsm, silc_client_command_reply_wait);
2430 return SILC_FSM_CONTINUE;
2433 /* Get authentication data */
2435 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2437 SilcClientCommandContext cmd = fsm_context;
2438 SilcClientConnection conn = cmd->conn;
2439 SilcClientCommandOper oper = cmd->context;
2441 silc_fsm_next(fsm, silc_client_command_oper_send);
2443 if (!oper || !oper->passphrase) {
2444 /* Encode the public key authentication payload */
2445 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2449 conn->internal->hash,
2450 conn->local_id, SILC_ID_CLIENT,
2451 silc_client_command_oper_sign_cb,
2456 /* Encode the password authentication payload */
2457 oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2458 oper->passphrase, oper->passphrase_len);
2460 return SILC_FSM_CONTINUE;
2463 /* OPER command. Used to obtain server operator privileges. */
2465 SILC_FSM_STATE(silc_client_command_oper)
2467 SilcClientCommandContext cmd = fsm_context;
2468 SilcClientConnection conn = cmd->conn;
2469 SilcClientCommandOper oper;
2471 if (cmd->argc < 2) {
2472 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2473 "Usage: /OPER <username> [-pubkey]");
2474 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2475 return SILC_FSM_FINISH;
2478 silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2480 /* Get passphrase */
2481 if (cmd->argc < 3) {
2482 oper = silc_calloc(1, sizeof(*oper));
2484 return SILC_FSM_FINISH;
2485 cmd->context = oper;
2486 SILC_FSM_CALL(conn->client->internal->
2487 ops->ask_passphrase(conn->client, conn,
2488 silc_client_command_oper_cb, cmd));
2491 return SILC_FSM_CONTINUE;
2494 /* SILCOPER command. Used to obtain router operator privileges. */
2496 SILC_FSM_STATE(silc_client_command_silcoper)
2498 SilcClientCommandContext cmd = fsm_context;
2499 SilcClientConnection conn = cmd->conn;
2500 SilcClientCommandOper oper;
2502 if (cmd->argc < 2) {
2503 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2504 "Usage: /SILCOPER <username> [-pubkey]");
2505 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2506 return SILC_FSM_FINISH;
2509 silc_fsm_next(fsm, silc_client_command_oper_send);
2511 /* Get passphrase */
2512 if (cmd->argc < 3) {
2513 oper = silc_calloc(1, sizeof(*oper));
2515 return SILC_FSM_FINISH;
2516 cmd->context = oper;
2517 SILC_FSM_CALL(conn->client->internal->
2518 ops->ask_passphrase(conn->client, conn,
2519 silc_client_command_oper_cb, cmd));
2522 return SILC_FSM_CONTINUE;
2525 /*********************************** BAN ************************************/
2527 /* Command BAN. This is used to manage the ban list of the channel. */
2529 SILC_FSM_STATE(silc_client_command_ban)
2531 SilcClientCommandContext cmd = fsm_context;
2532 SilcClientConnection conn = cmd->conn;
2533 SilcClient client = conn->client;
2534 SilcChannelEntry channel;
2535 SilcBuffer chidp, args = NULL;
2536 char *name, *ban = NULL;
2537 unsigned char action[1];
2538 SilcPublicKey pubkey = NULL;
2540 if (cmd->argc < 2) {
2541 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2542 "Usage: /BAN <channel> "
2543 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2544 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2548 if (cmd->argv[1][0] == '*') {
2549 if (!conn->current_channel) {
2550 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2554 channel = conn->current_channel;
2555 silc_client_ref_channel(client, conn, channel);
2557 name = cmd->argv[1];
2559 channel = silc_client_get_channel(conn->client, conn, name);
2561 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2566 if (cmd->argc == 3) {
2567 if (cmd->argv[2][0] == '+')
2572 /* Check if it is public key file to be added to invite list */
2573 silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
2580 args = silc_buffer_alloc_size(2);
2581 silc_buffer_format(args,
2582 SILC_STR_UI_SHORT(1),
2585 chidp = silc_public_key_payload_encode(NULL, pubkey);
2586 args = silc_argument_payload_encode_one(args,
2587 silc_buffer_datalen(chidp), 2);
2588 silc_buffer_free(chidp);
2589 silc_pkcs_public_key_free(pubkey);
2591 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2595 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2597 /* Send the command */
2598 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2599 1, silc_buffer_datalen(chidp),
2600 2, args ? action : NULL, args ? 1 : 0,
2601 3, silc_buffer_datalen(args));
2603 silc_buffer_free(chidp);
2604 silc_buffer_free(args);
2605 silc_client_unref_channel(client, conn, channel);
2607 /* Notify application */
2608 COMMAND(SILC_STATUS_OK);
2610 /** Wait for command reply */
2611 silc_fsm_next(fsm, silc_client_command_reply_wait);
2612 return SILC_FSM_CONTINUE;
2615 return SILC_FSM_FINISH;
2618 /********************************* DETACH ***********************************/
2620 /* Command DETACH. This is used to detach from the server */
2622 SILC_FSM_STATE(silc_client_command_detach)
2624 SilcClientCommandContext cmd = fsm_context;
2625 SilcClientConnection conn = cmd->conn;
2627 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2629 /* Notify application */
2630 COMMAND(SILC_STATUS_OK);
2632 /** Wait for command reply */
2633 silc_fsm_next(fsm, silc_client_command_reply_wait);
2634 return SILC_FSM_CONTINUE;
2637 /********************************** WATCH ***********************************/
2639 /* Command WATCH. */
2641 SILC_FSM_STATE(silc_client_command_watch)
2643 SilcClientCommandContext cmd = fsm_context;
2644 SilcClientConnection conn = cmd->conn;
2645 SilcBuffer args = NULL;
2647 const char *pubkey = NULL;
2648 SilcBool pubkey_add = TRUE;
2650 if (cmd->argc < 3) {
2651 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2655 if (!strcasecmp(cmd->argv[1], "-add")) {
2657 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2659 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2661 pubkey = cmd->argv[2] + 1;
2662 if (cmd->argv[2][0] == '-')
2665 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2673 if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
2674 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2675 "Could not load public key %s, check the filename", pubkey);
2676 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2680 args = silc_buffer_alloc_size(2);
2681 silc_buffer_format(args,
2682 SILC_STR_UI_SHORT(1),
2684 buffer = silc_public_key_payload_encode(NULL, pk);
2685 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2686 pubkey_add ? 0x00 : 0x01);
2687 silc_buffer_free(buffer);
2688 silc_pkcs_public_key_free(pk);
2691 /* If watching by nickname, resolve all users with that nickname so that
2692 we get their information immediately. */
2694 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2695 silc_client_command_resolve_dummy, NULL);
2697 /* Send the commmand */
2698 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2699 1, silc_buffer_datalen(conn->internal->
2701 type, pubkey ? args->data : cmd->argv[2],
2702 pubkey ? silc_buffer_len(args) :
2705 silc_buffer_free(args);
2707 /* Notify application */
2708 COMMAND(SILC_STATUS_OK);
2710 /** Wait for command reply */
2711 silc_fsm_next(fsm, silc_client_command_reply_wait);
2712 return SILC_FSM_CONTINUE;
2715 return SILC_FSM_FINISH;
2718 /********************************** LEAVE ***********************************/
2720 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2722 SILC_FSM_STATE(silc_client_command_leave)
2724 SilcClientCommandContext cmd = fsm_context;
2725 SilcClientConnection conn = cmd->conn;
2726 SilcClient client = conn->client;
2727 SilcChannelEntry channel;
2729 char *name, tmp[512];
2731 if (cmd->argc != 2) {
2732 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2733 "Usage: /LEAVE <channel>");
2734 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2738 if (cmd->argv[1][0] == '*') {
2739 if (!conn->current_channel) {
2740 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2744 if (client->internal->params->full_channel_names)
2745 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2747 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2748 conn->current_channel->channel_name,
2749 conn->current_channel->server[0] ? "@" : "",
2750 conn->current_channel->server);
2753 name = cmd->argv[1];
2756 /* Get the channel entry */
2757 channel = silc_client_get_channel(conn->client, conn, name);
2759 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2763 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2765 /* Send LEAVE command to the server */
2766 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2767 1, silc_buffer_datalen(idp));
2769 silc_buffer_free(idp);
2771 /* Notify application */
2772 COMMAND(SILC_STATUS_OK);
2774 if (conn->current_channel == channel)
2775 conn->current_channel = NULL;
2777 silc_client_unref_channel(client, conn, channel);
2779 /** Wait for command reply */
2780 silc_fsm_next(fsm, silc_client_command_reply_wait);
2781 return SILC_FSM_CONTINUE;
2784 return SILC_FSM_FINISH;
2787 /********************************** USERS ***********************************/
2789 /* Command USERS. Requests the USERS of the clients joined on requested
2792 SILC_FSM_STATE(silc_client_command_users)
2794 SilcClientCommandContext cmd = fsm_context;
2795 SilcClientConnection conn = cmd->conn;
2796 char *name, tmp[512];
2798 if (cmd->argc != 2) {
2799 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2800 "Usage: /USERS <channel>");
2801 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2805 if (cmd->argv[1][0] == '*') {
2806 if (!conn->current_channel) {
2807 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2811 if (conn->client->internal->params->full_channel_names)
2812 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2814 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2815 conn->current_channel->channel_name,
2816 conn->current_channel->server[0] ? "@" : "",
2817 conn->current_channel->server);
2820 name = cmd->argv[1];
2823 /* Send USERS command to the server */
2824 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2825 2, name, strlen(name));
2827 /* Notify application */
2828 COMMAND(SILC_STATUS_OK);
2830 /** Wait for command reply */
2831 silc_fsm_next(fsm, silc_client_command_reply_wait);
2832 return SILC_FSM_CONTINUE;
2835 return SILC_FSM_FINISH;
2838 /********************************* GETKEY ***********************************/
2840 /* Command GETKEY. Used to fetch remote client's public key. */
2842 SILC_FSM_STATE(silc_client_command_getkey)
2844 SilcClientCommandContext cmd = fsm_context;
2845 SilcClientConnection conn = cmd->conn;
2846 SilcClient client = conn->client;
2847 SilcClientEntry client_entry;
2848 SilcServerEntry server_entry;
2852 if (cmd->argc < 2) {
2853 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2854 "Usage: /GETKEY <nickname or server name>");
2855 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2856 return SILC_FSM_FINISH;
2859 /* Find client entry */
2860 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2862 /* Check whether user requested server */
2863 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2864 if (!server_entry) {
2865 if (cmd->resolved) {
2866 /* Resolving didn't find anything. We should never get here as
2867 errors are handled in the resolving callback. */
2868 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2869 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2870 return SILC_FSM_FINISH;
2873 /* No client or server exist with this name, query for both. */
2874 cmd->resolved = TRUE;
2875 SILC_FSM_CALL(silc_client_command_send(client, conn,
2876 SILC_COMMAND_IDENTIFY,
2877 silc_client_command_continue,
2880 strlen(cmd->argv[1]),
2882 strlen(cmd->argv[1])));
2885 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2886 silc_client_unref_server(client, conn, server_entry);
2888 client_entry = silc_dlist_get(clients);
2889 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2890 silc_client_list_free(client, conn, clients);
2893 /* Send the commmand */
2894 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2895 1, silc_buffer_datalen(idp));
2897 silc_buffer_free(idp);
2899 /* Notify application */
2900 COMMAND(SILC_STATUS_OK);
2902 /** Wait for command reply */
2903 silc_fsm_next(fsm, silc_client_command_reply_wait);
2904 return SILC_FSM_CONTINUE;
2907 /********************************* SERVICE **********************************/
2909 /* Command SERVICE. Negotiates service agreement with server. */
2910 /* XXX incomplete */
2912 SILC_FSM_STATE(silc_client_command_service)
2914 SilcClientCommandContext cmd = fsm_context;
2916 SilcClientConnection conn = cmd->conn;
2920 if (cmd->argc < 2) {
2921 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2922 "Usage: /SERVICE [<service name>] [-pubkey]");
2923 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2924 return SILC_FSM_FINISH;
2927 name = cmd->argv[1];
2929 /* Send SERVICE command to the server */
2930 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2931 ++conn->cmd_ident, 1,
2932 1, name, strlen(name));
2933 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2934 NULL, 0, NULL, NULL, buffer->data,
2936 silc_buffer_free(buffer);
2939 /* Notify application */
2940 COMMAND(SILC_STATUS_OK);
2942 /** Wait for command reply */
2943 silc_fsm_next(fsm, silc_client_command_reply_wait);
2944 return SILC_FSM_CONTINUE;
2947 /* Register all default commands provided by the client library for the
2950 void silc_client_commands_register(SilcClient client)
2952 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2955 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2956 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2957 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2958 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2959 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2960 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2961 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2962 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2963 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2964 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2965 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2966 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2967 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2968 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2969 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2970 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2971 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2972 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2973 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2974 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2975 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2976 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2977 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2978 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2979 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2980 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2981 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2984 /* Unregister all commands. */
2986 void silc_client_commands_unregister(SilcClient client)
2988 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2989 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2990 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2991 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2992 SILC_CLIENT_CMDU(list, LIST, "LIST");
2993 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2994 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2995 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2996 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2997 SILC_CLIENT_CMDU(info, INFO, "INFO");
2998 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2999 SILC_CLIENT_CMDU(ping, PING, "PING");
3000 SILC_CLIENT_CMDU(oper, OPER, "OPER");
3001 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
3002 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
3003 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
3004 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
3005 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3006 SILC_CLIENT_CMDU(kick, KICK, "KICK");
3007 SILC_CLIENT_CMDU(ban, BAN, "BAN");
3008 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3009 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3010 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3011 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3012 SILC_CLIENT_CMDU(users, USERS, "USERS");
3013 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3014 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3017 /****************** Client Side Incoming Command Handling *******************/
3020 SilcClientConnection conn;
3021 SilcUInt16 cmd_ident;
3022 } *SilcClientProcessWhois;
3024 /* Send reply to WHOIS from server */
3027 silc_client_command_process_whois_send(SilcBool success,
3028 const unsigned char *data,
3029 SilcUInt32 data_len, void *context)
3031 SilcClientProcessWhois w = context;
3032 SilcBufferStruct buffer;
3040 silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3042 /* Send the attributes back in COMMAND_REPLY packet */
3044 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3045 SILC_STATUS_OK, 0, w->cmd_ident,
3047 silc_buffer_len(&buffer));
3053 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3055 silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3056 silc_buffer_datalen(packet));
3058 silc_buffer_free(packet);
3062 /* Process WHOIS command from server */
3064 static void silc_client_command_process_whois(SilcClient client,
3065 SilcClientConnection conn,
3066 SilcCommandPayload payload,
3067 SilcArgumentPayload args)
3069 SilcClientProcessWhois w;
3074 SILC_LOG_DEBUG(("Received WHOIS command"));
3076 /* Try to take the Requested Attributes */
3077 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3081 attrs = silc_attribute_payload_parse(tmp, tmp_len);
3085 w = silc_calloc(1, sizeof(*w));
3087 silc_attribute_payload_list_free(attrs);
3091 w->cmd_ident = silc_command_get_ident(payload);
3093 /* Process requested attributes */
3094 silc_client_attributes_process(client, conn, attrs,
3095 silc_client_command_process_whois_send, w);
3096 silc_attribute_payload_list_free(attrs);
3099 /* Client is able to receive some command packets even though they are
3100 special case. Server may send WHOIS command to the client to retrieve
3101 Requested Attributes information for WHOIS query the server is
3102 processing. This function currently handles only the WHOIS command,
3103 but if in the future more commands may arrive then this can be made
3104 to support other commands too. */
3106 SILC_FSM_STATE(silc_client_command)
3108 SilcClientConnection conn = fsm_context;
3109 SilcClient client = conn->client;
3110 SilcPacket packet = state_context;
3111 SilcCommandPayload payload;
3112 SilcCommand command;
3113 SilcArgumentPayload args;
3115 /* Get command payload from packet */
3116 payload = silc_command_payload_parse(packet->buffer.data,
3117 silc_buffer_len(&packet->buffer));
3119 SILC_LOG_DEBUG(("Bad command packet"));
3120 return SILC_FSM_FINISH;
3124 args = silc_command_get_args(payload);
3126 /* Get the command */
3127 command = silc_command_get(payload);
3130 case SILC_COMMAND_WHOIS:
3131 /* Ignore everything if requested by application */
3132 if (conn->internal->params.ignore_requested_attributes)
3135 silc_client_command_process_whois(client, conn, payload, args);
3142 silc_command_payload_free(payload);
3143 return SILC_FSM_FINISH;