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(NULL, 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;
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 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1056 /* Find client entry */
1057 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1060 /* Resolve client information */
1061 SILC_FSM_CALL(silc_client_get_clients(
1062 client, conn, nickname, NULL,
1063 silc_client_command_resolve_continue,
1066 client_entry = silc_dlist_get(clients);
1068 if (cmd->argv[2][0] == '+')
1073 /* Check if it is public key file to be added to invite list */
1074 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1075 invite = cmd->argv[2];
1082 args = silc_buffer_alloc_size(2);
1083 silc_buffer_format(args,
1084 SILC_STR_UI_SHORT(1),
1087 chidp = silc_public_key_payload_encode(NULL, pubkey);
1088 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1089 silc_buffer_len(chidp), 2);
1090 silc_buffer_free(chidp);
1091 silc_pkcs_public_key_free(pubkey);
1093 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1097 /* Send the command */
1098 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1100 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1101 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1102 1, silc_buffer_datalen(chidp),
1103 2, silc_buffer_datalen(clidp),
1104 3, args ? action : NULL, args ? 1 : 0,
1105 4, silc_buffer_datalen(args));
1106 silc_buffer_free(clidp);
1108 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1109 1, silc_buffer_datalen(chidp),
1110 3, args ? action : NULL, args ? 1 : 0,
1111 4, silc_buffer_datalen(args));
1114 silc_buffer_free(chidp);
1115 silc_buffer_free(args);
1116 silc_free(nickname);
1117 silc_client_list_free(client, conn, clients);
1118 silc_client_unref_channel(client, conn, channel);
1120 /* Notify application */
1121 COMMAND(SILC_STATUS_OK);
1123 /** Wait for command reply */
1124 silc_fsm_next(fsm, silc_client_command_reply_wait);
1125 return SILC_FSM_CONTINUE;
1128 silc_free(nickname);
1129 return SILC_FSM_FINISH;
1132 /********************************** QUIT ************************************/
1134 /* Close the connection */
1136 SILC_FSM_STATE(silc_client_command_quit_final)
1138 SilcClientCommandContext cmd = fsm_context;
1139 SilcClientConnection conn = cmd->conn;
1141 SILC_LOG_DEBUG(("Quitting"));
1143 /* Notify application */
1144 COMMAND(SILC_STATUS_OK);
1146 /* Signal to close connection */
1147 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1148 if (!conn->internal->disconnected) {
1149 conn->internal->disconnected = TRUE;
1150 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1153 return SILC_FSM_FINISH;
1156 /* Command QUIT. Closes connection with current server. */
1158 SILC_FSM_STATE(silc_client_command_quit)
1160 SilcClientCommandContext cmd = fsm_context;
1161 SilcClientConnection conn = cmd->conn;
1164 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1165 1, cmd->argv[1], cmd->argv_lens[1]);
1167 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1169 /* Sleep for a while */
1172 /* We close the connection with a little timeout */
1173 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1174 return SILC_FSM_WAIT;
1177 /********************************** KILL ************************************/
1179 /* Signature callback */
1181 static void silc_client_command_kill_signed(const SilcBuffer buffer,
1184 SilcClientCommandContext cmd = context;
1187 silc_fsm_finish(&cmd->thread);
1191 silc_fsm_set_state_context(&cmd->thread, buffer);
1192 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1195 /* Send KILL command */
1197 SILC_FSM_STATE(silc_client_command_kill_send)
1199 SilcClientCommandContext cmd = fsm_context;
1200 SilcClientConnection conn = cmd->conn;
1201 SilcClient client = conn->client;
1202 SilcBuffer idp, auth = state_context;
1203 SilcClientEntry target = cmd->context;
1204 char *comment = NULL;
1207 if (strcasecmp(cmd->argv[2], "-pubkey"))
1208 comment = cmd->argv[2];
1210 /* Send the KILL command to the server */
1211 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1212 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1213 1, silc_buffer_datalen(idp),
1214 2, comment, comment ? strlen(comment) : 0,
1215 3, silc_buffer_datalen(auth));
1217 silc_buffer_free(idp);
1218 silc_client_unref_client(client, conn, target);
1220 /* Notify application */
1221 COMMAND(SILC_STATUS_OK);
1223 /** Wait for command reply */
1224 silc_fsm_next(fsm, silc_client_command_reply_wait);
1225 return SILC_FSM_CONTINUE;
1228 /* Command KILL. Router operator can use this command to remove an client
1229 fromthe SILC Network. */
1231 SILC_FSM_STATE(silc_client_command_kill)
1233 SilcClientCommandContext cmd = fsm_context;
1234 SilcClientConnection conn = cmd->conn;
1235 SilcClient client = conn->client;
1236 SilcClientEntry target;
1238 char *nickname = NULL;
1240 if (cmd->argc < 2) {
1241 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1242 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1243 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1244 return SILC_FSM_FINISH;
1247 /* Parse the typed nickname. */
1248 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1249 return SILC_FSM_FINISH;
1251 /* Get the target client */
1252 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1254 /* Resolve client information */
1255 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1256 silc_client_command_resolve_continue,
1259 target = silc_dlist_get(clients);
1260 cmd->context = silc_client_ref_client(client, conn, target);
1262 silc_free(nickname);
1263 silc_client_list_free(client, conn, clients);
1266 silc_fsm_next(fsm, silc_client_command_kill_send);
1268 if (cmd->argc >= 3) {
1269 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1270 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1271 /* Encode the public key authentication payload */
1272 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1276 conn->internal->sha1hash,
1277 &target->id, SILC_ID_CLIENT,
1278 silc_client_command_kill_signed,
1284 return SILC_FSM_CONTINUE;
1287 /********************************** INFO ************************************/
1289 /* Command INFO. Request information about specific server. If specific
1290 server is not provided the current server is used. */
1292 SILC_FSM_STATE(silc_client_command_info)
1294 SilcClientCommandContext cmd = fsm_context;
1295 SilcClientConnection conn = cmd->conn;
1297 /* Send the command */
1299 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1300 1, cmd->argv[1], cmd->argv_lens[1]);
1302 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1304 /* Notify application */
1305 COMMAND(SILC_STATUS_OK);
1307 /** Wait for command reply */
1308 silc_fsm_next(fsm, silc_client_command_reply_wait);
1309 return SILC_FSM_CONTINUE;
1312 /********************************** STATS ***********************************/
1314 /* Command STATS. Shows server and network statistics. */
1316 SILC_FSM_STATE(silc_client_command_stats)
1318 SilcClientCommandContext cmd = fsm_context;
1319 SilcClientConnection conn = cmd->conn;
1321 /* Send the command */
1322 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1323 1, silc_buffer_datalen(conn->internal->
1326 /* Notify application */
1327 COMMAND(SILC_STATUS_OK);
1329 /** Wait for command reply */
1330 silc_fsm_next(fsm, silc_client_command_reply_wait);
1331 return SILC_FSM_CONTINUE;
1334 /********************************** PING ************************************/
1336 /* Command PING. Sends ping to server. */
1338 SILC_FSM_STATE(silc_client_command_ping)
1340 SilcClientCommandContext cmd = fsm_context;
1341 SilcClientConnection conn = cmd->conn;
1343 if (cmd->argc < 2) {
1344 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1345 return SILC_FSM_FINISH;
1348 /* Send the command */
1349 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1350 1, silc_buffer_datalen(conn->internal->
1353 /* Save ping time */
1354 cmd->context = SILC_64_TO_PTR(silc_time());
1356 /* Notify application */
1357 COMMAND(SILC_STATUS_OK);
1359 /** Wait for command reply */
1360 silc_fsm_next(fsm, silc_client_command_reply_wait);
1361 return SILC_FSM_CONTINUE;
1364 /********************************** JOIN ************************************/
1370 } *SilcClientJoinContext;
1372 /* Signature callback */
1374 static void silc_client_command_join_signed(const SilcBuffer buffer,
1377 SilcClientCommandContext cmd = context;
1378 SilcClientJoinContext j = cmd->context;
1381 silc_fsm_finish(&cmd->thread);
1386 j->auth = silc_buffer_copy(buffer);
1388 j->cauth = silc_buffer_copy(buffer);
1390 SILC_FSM_CALL_CONTINUE(&cmd->thread);
1393 /* Command JOIN. Joins to a channel. */
1395 SILC_FSM_STATE(silc_client_command_join)
1397 SilcClientCommandContext cmd2, cmd = fsm_context;
1398 SilcClientConnection conn = cmd->conn;
1399 SilcClient client = conn->client;
1400 SilcChannelEntry channel = NULL;
1401 SilcClientJoinContext j = cmd->context;
1402 SilcBuffer auth = NULL, cauth = NULL;
1403 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1404 int i, passphrase_len = 0;
1406 if (cmd->argc < 2) {
1407 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1411 /* See if we have joined to the requested channel already */
1412 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1413 if (channel && silc_client_on_channel(channel, conn->local_entry))
1416 /* If NICK command is active, wait for it to finish before sending JOIN.
1417 To avoid problems locally with changing IDs while joining, we do this. */
1418 silc_mutex_lock(conn->internal->lock);
1419 silc_list_start(conn->internal->pending_commands);
1420 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1421 if (cmd2->cmd == SILC_COMMAND_NICK) {
1422 silc_mutex_unlock(conn->internal->lock);
1423 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1424 return SILC_FSM_WAIT;
1427 silc_mutex_unlock(conn->internal->lock);
1429 if (cmd->argv_lens[1] > 256)
1430 cmd->argv_lens[1] = 256;
1432 name = cmd->argv[1];
1434 for (i = 2; i < cmd->argc; i++) {
1435 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1436 cipher = cmd->argv[++i];
1437 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1438 hmac = cmd->argv[++i];
1439 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1440 if (!j || !j->auth) {
1442 j = silc_calloc(1, sizeof(*j));
1448 silc_free(passphrase);
1449 silc_client_unref_channel(client, conn, channel);
1450 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1454 conn->internal->sha1hash,
1457 silc_client_command_join_signed,
1461 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1462 SilcPublicKey pubkey = conn->public_key;
1463 SilcPrivateKey privkey = conn->private_key;
1464 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], pubdata[128];
1467 if (!j || !j->cauth) {
1469 j = silc_calloc(1, sizeof(*j));
1475 if (cmd->argc >= i + 3) {
1477 if (cmd->argc >= i + 4) {
1478 pass = cmd->argv[i + 3];
1481 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1482 &pubkey, &privkey)) {
1483 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1484 "Could not load key pair, check your arguments");
1485 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1492 pk = silc_pkcs_public_key_encode(NULL, pubkey, &pk_len);
1493 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1495 silc_rng_get_rn_data(conn->client->rng, sizeof(pubdata), pubdata,
1497 memcpy(pubdata, pkhash, 20);
1498 silc_free(passphrase);
1499 silc_client_unref_channel(client, conn, channel);
1500 SILC_FSM_CALL(silc_auth_public_key_auth_generate_wpub(
1502 pubdata, sizeof(pubdata),
1503 conn->internal->sha1hash,
1506 silc_client_command_join_signed,
1511 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1512 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1513 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1514 cmd->argv_lens[i], 0);
1515 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1516 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1517 0, pu8, passphrase_len);
1520 passphrase = strdup(cmd->argv[i]);
1521 passphrase_len = cmd->argv_lens[i];
1526 /* Send JOIN command to the server */
1527 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1528 1, name, strlen(name),
1529 2, silc_buffer_datalen(conn->internal->
1531 3, passphrase, passphrase_len,
1532 4, cipher, cipher ? strlen(cipher) : 0,
1533 5, hmac, hmac ? strlen(hmac) : 0,
1534 6, silc_buffer_datalen(auth),
1535 7, silc_buffer_datalen(cauth));
1538 memset(passphrase, 0, strlen(passphrase));
1539 silc_free(passphrase);
1540 silc_client_unref_channel(client, conn, channel);
1542 silc_buffer_free(j->auth);
1543 silc_buffer_free(j->cauth);
1547 /* Notify application */
1548 COMMAND(SILC_STATUS_OK);
1550 /** Wait for command reply */
1551 silc_fsm_next(fsm, silc_client_command_reply_wait);
1552 return SILC_FSM_CONTINUE;
1555 silc_client_unref_channel(client, conn, channel);
1556 return SILC_FSM_FINISH;
1559 /********************************** MOTD ************************************/
1561 /* MOTD command. Requests motd from server. */
1563 SILC_FSM_STATE(silc_client_command_motd)
1565 SilcClientCommandContext cmd = fsm_context;
1566 SilcClientConnection conn = cmd->conn;
1568 if (cmd->argc < 1 || cmd->argc > 2) {
1569 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1570 "Usage: /MOTD [<server>]");
1571 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1572 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1573 return SILC_FSM_FINISH;
1576 /* Send the command */
1578 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1579 1, conn->remote_host,
1580 strlen(conn->remote_host));
1582 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1583 1, cmd->argv[1], cmd->argv_lens[1]);
1585 /* Notify application */
1586 COMMAND(SILC_STATUS_OK);
1588 /** Wait for command reply */
1589 silc_fsm_next(fsm, silc_client_command_reply_wait);
1590 return SILC_FSM_CONTINUE;
1593 /********************************** UMODE ***********************************/
1595 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1596 modes as client cannot set itself server/router operator privileges. */
1598 SILC_FSM_STATE(silc_client_command_umode)
1600 SilcClientCommandContext cmd = fsm_context;
1601 SilcClientConnection conn = cmd->conn;
1602 unsigned char *cp, modebuf[4];
1603 SilcUInt32 mode, add, len;
1606 if (cmd->argc < 2) {
1607 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1608 "Usage: /UMODE +|-<modes>");
1609 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1610 return SILC_FSM_FINISH;
1613 mode = conn->local_entry->mode;
1615 /* Are we adding or removing mode */
1616 if (cmd->argv[1][0] == '-')
1622 cp = cmd->argv[1] + 1;
1624 for (i = 0; i < len; i++) {
1629 mode |= SILC_UMODE_SERVER_OPERATOR;
1630 mode |= SILC_UMODE_ROUTER_OPERATOR;
1631 mode |= SILC_UMODE_GONE;
1632 mode |= SILC_UMODE_INDISPOSED;
1633 mode |= SILC_UMODE_BUSY;
1634 mode |= SILC_UMODE_PAGE;
1635 mode |= SILC_UMODE_HYPER;
1636 mode |= SILC_UMODE_ROBOT;
1637 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1638 mode |= SILC_UMODE_REJECT_WATCHING;
1640 mode = SILC_UMODE_NONE;
1645 mode |= SILC_UMODE_SERVER_OPERATOR;
1647 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1651 mode |= SILC_UMODE_ROUTER_OPERATOR;
1653 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1657 mode |= SILC_UMODE_GONE;
1659 mode &= ~SILC_UMODE_GONE;
1663 mode |= SILC_UMODE_INDISPOSED;
1665 mode &= ~SILC_UMODE_INDISPOSED;
1669 mode |= SILC_UMODE_BUSY;
1671 mode &= ~SILC_UMODE_BUSY;
1675 mode |= SILC_UMODE_PAGE;
1677 mode &= ~SILC_UMODE_PAGE;
1681 mode |= SILC_UMODE_HYPER;
1683 mode &= ~SILC_UMODE_HYPER;
1687 mode |= SILC_UMODE_ROBOT;
1689 mode &= ~SILC_UMODE_ROBOT;
1693 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1695 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1699 mode |= SILC_UMODE_REJECT_WATCHING;
1701 mode &= ~SILC_UMODE_REJECT_WATCHING;
1705 mode |= SILC_UMODE_BLOCK_INVITE;
1707 mode &= ~SILC_UMODE_BLOCK_INVITE;
1710 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1711 return SILC_FSM_FINISH;
1716 SILC_PUT32_MSB(mode, modebuf);
1718 /* Send the command */
1719 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1720 1, silc_buffer_datalen(conn->internal->
1722 2, modebuf, sizeof(modebuf));
1724 /* Notify application */
1725 COMMAND(SILC_STATUS_OK);
1727 /** Wait for command reply */
1728 silc_fsm_next(fsm, silc_client_command_reply_wait);
1729 return SILC_FSM_CONTINUE;
1732 /********************************** CMODE ***********************************/
1734 /* Signature callback */
1736 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1739 SilcClientCommandContext cmd = context;
1742 silc_fsm_finish(&cmd->thread);
1746 silc_fsm_set_state_context(&cmd->thread, buffer);
1747 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1750 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1751 can be set several at once. Those modes that require argument must be set
1752 separately (unless set with modes that does not require arguments). */
1754 SILC_FSM_STATE(silc_client_command_cmode)
1756 SilcClientCommandContext cmd = fsm_context;
1757 SilcClientConnection conn = cmd->conn;
1758 SilcClient client = conn->client;
1759 SilcBuffer auth = state_context;
1760 SilcChannelEntry channel = NULL;
1761 SilcBuffer chidp, pk = NULL;
1762 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1763 SilcUInt32 mode, add, type, len, arg_len = 0;
1766 if (cmd->argc < 3) {
1767 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1768 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1769 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1773 if (cmd->argv[1][0] == '*') {
1774 if (!conn->current_channel) {
1775 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1779 channel = conn->current_channel;
1780 silc_client_ref_channel(client, conn, channel);
1782 name = cmd->argv[1];
1784 channel = silc_client_get_channel(conn->client, conn, name);
1786 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1791 mode = channel->mode;
1793 /* Are we adding or removing mode */
1794 if (cmd->argv[2][0] == '-')
1799 /* Argument type to be sent to server */
1803 cp = cmd->argv[2] + 1;
1805 for (i = 0; i < len; i++) {
1809 mode |= SILC_CHANNEL_MODE_PRIVATE;
1811 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1815 mode |= SILC_CHANNEL_MODE_SECRET;
1817 mode &= ~SILC_CHANNEL_MODE_SECRET;
1821 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1823 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1827 mode |= SILC_CHANNEL_MODE_INVITE;
1829 mode &= ~SILC_CHANNEL_MODE_INVITE;
1833 mode |= SILC_CHANNEL_MODE_TOPIC;
1835 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1839 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1841 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1845 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1847 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1852 mode |= SILC_CHANNEL_MODE_ULIMIT;
1854 if (cmd->argc < 4) {
1855 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1856 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1857 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1860 ll = atoi(cmd->argv[3]);
1861 SILC_PUT32_MSB(ll, tmp);
1865 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1870 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1872 if (cmd->argc < 4) {
1873 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1874 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1875 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1879 arg_len = cmd->argv_lens[3];
1881 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1886 mode |= SILC_CHANNEL_MODE_CIPHER;
1888 if (cmd->argc < 4) {
1889 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1890 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1891 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1895 arg_len = cmd->argv_lens[3];
1897 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1902 mode |= SILC_CHANNEL_MODE_HMAC;
1904 if (cmd->argc < 4) {
1905 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1906 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1907 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1911 arg_len = cmd->argv_lens[3];
1913 mode &= ~SILC_CHANNEL_MODE_HMAC;
1919 SilcPublicKey pubkey = conn->public_key;
1920 SilcPrivateKey privkey = conn->private_key;
1922 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1925 if (cmd->argc >= 5) {
1928 pass = cmd->argv[5];
1929 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1930 &pubkey, &privkey)) {
1931 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1932 "Could not load key pair, check your arguments");
1933 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1938 pk = silc_public_key_payload_encode(NULL, pubkey);
1939 silc_client_unref_channel(client, conn, channel);
1940 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1943 conn->internal->sha1hash,
1946 silc_client_command_cmode_signed,
1950 arg = silc_buffer_data(auth);
1951 arg_len = silc_buffer_len(auth);
1953 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1959 SilcBool chadd = FALSE;
1960 SilcPublicKey chpk = NULL;
1962 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1965 if (cmd->argc == 3) {
1966 /* Send empty command to receive the public key list. */
1967 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1968 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1970 1, silc_buffer_datalen(chidp));
1971 silc_buffer_free(chidp);
1972 silc_client_unref_channel(client, conn, channel);
1974 /* Notify application */
1975 COMMAND(SILC_STATUS_OK);
1977 /** Wait for command reply */
1978 silc_fsm_next(fsm, silc_client_command_reply_wait);
1979 return SILC_FSM_CONTINUE;
1982 if (cmd->argc >= 4) {
1983 auth = silc_buffer_alloc_size(2);
1984 silc_buffer_format(auth,
1985 SILC_STR_UI_SHORT(cmd->argc - 3),
1989 for (k = 3; k < cmd->argc; k++) {
1990 if (cmd->argv[k][0] == '+')
1992 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1993 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1994 "Could not load public key %s, check the filename",
1996 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1997 silc_buffer_free(auth);
2002 pk = silc_public_key_payload_encode(NULL, chpk);
2003 auth = silc_argument_payload_encode_one(auth,
2004 silc_buffer_datalen(pk),
2005 chadd ? 0x00 : 0x01);
2006 silc_pkcs_public_key_free(chpk);
2007 silc_buffer_free(pk);
2012 arg = silc_buffer_data(auth);
2013 arg_len = silc_buffer_len(auth);
2015 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2019 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2025 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2026 SILC_PUT32_MSB(mode, modebuf);
2028 /* Send the command. We support sending only one mode at once that
2029 requires an argument. */
2031 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2032 1, silc_buffer_datalen(chidp),
2033 2, modebuf, sizeof(modebuf),
2035 8, silc_buffer_datalen(pk));
2037 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2038 1, silc_buffer_datalen(chidp),
2039 2, modebuf, sizeof(modebuf));
2042 silc_buffer_free(chidp);
2043 silc_buffer_free(auth);
2044 silc_buffer_free(pk);
2045 silc_client_unref_channel(client, conn, channel);
2047 /* Notify application */
2048 COMMAND(SILC_STATUS_OK);
2050 /** Wait for command reply */
2051 silc_fsm_next(fsm, silc_client_command_reply_wait);
2052 return SILC_FSM_CONTINUE;
2055 silc_client_unref_channel(client, conn, channel);
2056 return SILC_FSM_FINISH;
2059 /********************************* CUMODE ***********************************/
2061 /* Signature callback */
2063 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2066 SilcClientCommandContext cmd = context;
2069 silc_fsm_finish(&cmd->thread);
2073 silc_fsm_set_state_context(&cmd->thread, buffer);
2074 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2077 /* CUMODE command. Changes client's mode on a channel. */
2079 SILC_FSM_STATE(silc_client_command_cumode)
2081 SilcClientCommandContext cmd = fsm_context;
2082 SilcClientConnection conn = cmd->conn;
2083 SilcClient client = conn->client;
2084 SilcBuffer auth = state_context;
2085 SilcChannelEntry channel = NULL;
2086 SilcChannelUser chu;
2087 SilcClientEntry client_entry;
2088 SilcBuffer clidp, chidp;
2089 SilcDList clients = NULL;
2090 unsigned char *name, *cp, modebuf[4];
2091 SilcUInt32 mode = 0, add, len;
2092 char *nickname = NULL;
2095 if (cmd->argc < 4) {
2096 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2097 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2098 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2102 if (cmd->argv[1][0] == '*') {
2103 if (!conn->current_channel) {
2104 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2108 channel = conn->current_channel;
2109 silc_client_ref_channel(client, conn, channel);
2111 name = cmd->argv[1];
2113 channel = silc_client_get_channel(conn->client, conn, name);
2115 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2120 /* Parse the typed nickname. */
2121 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
2123 /* Find client entry */
2124 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2126 /* Resolve client information */
2127 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2128 silc_client_command_resolve_continue,
2131 client_entry = silc_dlist_get(clients);
2133 /* Get the current mode */
2134 chu = silc_client_on_channel(channel, client_entry);
2138 /* Are we adding or removing mode */
2139 if (cmd->argv[2][0] == '-')
2145 cp = cmd->argv[2] + 1;
2147 for (i = 0; i < len; i++) {
2151 mode |= SILC_CHANNEL_UMODE_CHANFO;
2152 mode |= SILC_CHANNEL_UMODE_CHANOP;
2153 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2154 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2155 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2157 mode = SILC_CHANNEL_UMODE_NONE;
2163 SilcPublicKey pubkey = conn->public_key;
2164 SilcPrivateKey privkey = conn->private_key;
2166 if (cmd->argc >= 6) {
2169 pass = cmd->argv[6];
2170 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2171 &pubkey, &privkey)) {
2172 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2173 "Could not load key pair, check your arguments");
2174 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2179 silc_free(nickname);
2180 silc_client_list_free(client, conn, clients);
2181 silc_client_unref_channel(client, conn, channel);
2183 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2186 conn->internal->sha1hash,
2189 silc_client_command_cumode_signed,
2194 mode |= SILC_CHANNEL_UMODE_CHANFO;
2196 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2201 mode |= SILC_CHANNEL_UMODE_CHANOP;
2203 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2207 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2209 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2213 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2215 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2219 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2221 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2225 mode |= SILC_CHANNEL_UMODE_QUIET;
2227 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2230 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2236 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2237 SILC_PUT32_MSB(mode, modebuf);
2238 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2240 /* Send the command packet. We support sending only one mode at once
2241 that requires an argument. */
2242 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2243 1, silc_buffer_datalen(chidp),
2245 3, silc_buffer_datalen(clidp),
2246 4, silc_buffer_datalen(auth));
2248 silc_buffer_free(chidp);
2249 silc_buffer_free(clidp);
2251 silc_buffer_free(auth);
2252 silc_free(nickname);
2253 silc_client_list_free(client, conn, clients);
2254 silc_client_unref_channel(client, conn, channel);
2256 /* Notify application */
2257 COMMAND(SILC_STATUS_OK);
2259 /** Wait for command reply */
2260 silc_fsm_next(fsm, silc_client_command_reply_wait);
2261 return SILC_FSM_CONTINUE;
2264 silc_client_unref_channel(client, conn, channel);
2265 silc_client_list_free(client, conn, clients);
2266 silc_free(nickname);
2267 return SILC_FSM_FINISH;
2270 /********************************** KICK ************************************/
2272 /* KICK command. Kicks a client out of channel. */
2274 SILC_FSM_STATE(silc_client_command_kick)
2276 SilcClientCommandContext cmd = fsm_context;
2277 SilcClientConnection conn = cmd->conn;
2278 SilcClient client = conn->client;
2279 SilcChannelEntry channel = NULL;
2280 SilcBuffer idp, idp2;
2281 SilcClientEntry target;
2282 SilcDList clients = NULL;
2283 char *name, tmp[512];
2285 if (cmd->argc < 3) {
2286 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2287 "Usage: /KICK <channel> <nickname> [<comment>]");
2288 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2292 if (cmd->argv[1][0] == '*') {
2293 if (!conn->current_channel) {
2294 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2298 if (client->internal->params->full_channel_names)
2299 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2301 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2302 conn->current_channel->channel_name,
2303 conn->current_channel->server[0] ? "@" : "",
2304 conn->current_channel->server);
2307 name = cmd->argv[1];
2310 if (!conn->current_channel) {
2311 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2315 /* Get the Channel ID of the channel */
2316 channel = silc_client_get_channel(conn->client, conn, name);
2318 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2322 /* Get the target client */
2323 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2325 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2326 "No such client: %s", cmd->argv[2]);
2327 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2330 target = silc_dlist_get(clients);
2332 /* Send KICK command to the server */
2333 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2334 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2336 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2337 1, silc_buffer_datalen(idp),
2338 2, silc_buffer_datalen(idp2));
2340 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2341 1, silc_buffer_datalen(idp),
2342 2, silc_buffer_datalen(idp2),
2343 3, cmd->argv[3], strlen(cmd->argv[3]));
2345 silc_buffer_free(idp);
2346 silc_buffer_free(idp2);
2347 silc_client_list_free(client, conn, clients);
2348 silc_client_unref_channel(client, conn, channel);
2350 /* Notify application */
2351 COMMAND(SILC_STATUS_OK);
2353 /** Wait for command reply */
2354 silc_fsm_next(fsm, silc_client_command_reply_wait);
2355 return SILC_FSM_CONTINUE;
2358 silc_client_unref_channel(client, conn, channel);
2359 return SILC_FSM_FINISH;
2362 /***************************** OPER & SILCOPER ******************************/
2365 unsigned char *passphrase;
2366 SilcUInt32 passphrase_len;
2368 } *SilcClientCommandOper;
2370 /* Ask passphrase callback */
2372 static void silc_client_command_oper_cb(const unsigned char *data,
2373 SilcUInt32 data_len, void *context)
2375 SilcClientCommandContext cmd = context;
2376 SilcClientCommandOper oper = cmd->context;
2378 if (data && data_len)
2379 oper->passphrase = silc_memdup(data, data_len);
2380 oper->passphrase_len = data_len;
2383 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2386 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2389 SilcClientCommandContext cmd = context;
2390 SilcClientCommandOper oper = cmd->context;
2393 oper->auth = silc_buffer_copy(data);
2396 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2399 /* Send OPER/SILCOPER command */
2401 SILC_FSM_STATE(silc_client_command_oper_send)
2403 SilcClientCommandContext cmd = fsm_context;
2404 SilcClientConnection conn = cmd->conn;
2405 SilcClientCommandOper oper = cmd->context;
2406 SilcBuffer auth = oper ? oper->auth : NULL;
2408 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2409 1, cmd->argv[1], strlen(cmd->argv[1]),
2410 2, silc_buffer_datalen(auth));
2412 silc_buffer_clear(auth);
2413 silc_buffer_free(auth);
2415 silc_free(oper->passphrase);
2419 /* Notify application */
2420 COMMAND(SILC_STATUS_OK);
2422 /** Wait for command reply */
2423 silc_fsm_next(fsm, silc_client_command_reply_wait);
2424 return SILC_FSM_CONTINUE;
2427 /* Get authentication data */
2429 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2431 SilcClientCommandContext cmd = fsm_context;
2432 SilcClientConnection conn = cmd->conn;
2433 SilcClientCommandOper oper = cmd->context;
2435 silc_fsm_next(fsm, silc_client_command_oper_send);
2437 if (!oper || !oper->passphrase) {
2438 /* Encode the public key authentication payload */
2439 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2443 conn->internal->hash,
2444 conn->local_id, SILC_ID_CLIENT,
2445 silc_client_command_oper_sign_cb,
2450 /* Encode the password authentication payload */
2451 oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2452 oper->passphrase, oper->passphrase_len);
2454 return SILC_FSM_CONTINUE;
2457 /* OPER command. Used to obtain server operator privileges. */
2459 SILC_FSM_STATE(silc_client_command_oper)
2461 SilcClientCommandContext cmd = fsm_context;
2462 SilcClientConnection conn = cmd->conn;
2463 SilcClientCommandOper oper;
2465 if (cmd->argc < 2) {
2466 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2467 "Usage: /OPER <username> [-pubkey]");
2468 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2469 return SILC_FSM_FINISH;
2472 silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2474 /* Get passphrase */
2475 if (cmd->argc < 3) {
2476 oper = silc_calloc(1, sizeof(*oper));
2478 return SILC_FSM_FINISH;
2479 cmd->context = oper;
2480 SILC_FSM_CALL(conn->client->internal->
2481 ops->ask_passphrase(conn->client, conn,
2482 silc_client_command_oper_cb, cmd));
2485 return SILC_FSM_CONTINUE;
2488 /* SILCOPER command. Used to obtain router operator privileges. */
2490 SILC_FSM_STATE(silc_client_command_silcoper)
2492 SilcClientCommandContext cmd = fsm_context;
2493 SilcClientConnection conn = cmd->conn;
2494 SilcClientCommandOper oper;
2496 if (cmd->argc < 2) {
2497 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2498 "Usage: /SILCOPER <username> [-pubkey]");
2499 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2500 return SILC_FSM_FINISH;
2503 silc_fsm_next(fsm, silc_client_command_oper_send);
2505 /* Get passphrase */
2506 if (cmd->argc < 3) {
2507 oper = silc_calloc(1, sizeof(*oper));
2509 return SILC_FSM_FINISH;
2510 cmd->context = oper;
2511 SILC_FSM_CALL(conn->client->internal->
2512 ops->ask_passphrase(conn->client, conn,
2513 silc_client_command_oper_cb, cmd));
2516 return SILC_FSM_CONTINUE;
2519 /*********************************** BAN ************************************/
2521 /* Command BAN. This is used to manage the ban list of the channel. */
2523 SILC_FSM_STATE(silc_client_command_ban)
2525 SilcClientCommandContext cmd = fsm_context;
2526 SilcClientConnection conn = cmd->conn;
2527 SilcClient client = conn->client;
2528 SilcChannelEntry channel;
2529 SilcBuffer chidp, args = NULL;
2530 char *name, *ban = NULL;
2531 unsigned char action[1];
2532 SilcPublicKey pubkey = NULL;
2534 if (cmd->argc < 2) {
2535 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2536 "Usage: /BAN <channel> "
2537 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2538 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2542 if (cmd->argv[1][0] == '*') {
2543 if (!conn->current_channel) {
2544 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2548 channel = conn->current_channel;
2549 silc_client_ref_channel(client, conn, channel);
2551 name = cmd->argv[1];
2553 channel = silc_client_get_channel(conn->client, conn, name);
2555 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2560 if (cmd->argc == 3) {
2561 if (cmd->argv[2][0] == '+')
2566 /* Check if it is public key file to be added to invite list */
2567 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2574 args = silc_buffer_alloc_size(2);
2575 silc_buffer_format(args,
2576 SILC_STR_UI_SHORT(1),
2579 chidp = silc_public_key_payload_encode(NULL, pubkey);
2580 args = silc_argument_payload_encode_one(args,
2581 silc_buffer_datalen(chidp), 2);
2582 silc_buffer_free(chidp);
2583 silc_pkcs_public_key_free(pubkey);
2585 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2589 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2591 /* Send the command */
2592 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2593 1, silc_buffer_datalen(chidp),
2594 2, args ? action : NULL, args ? 1 : 0,
2595 3, silc_buffer_datalen(args));
2597 silc_buffer_free(chidp);
2598 silc_buffer_free(args);
2599 silc_client_unref_channel(client, conn, channel);
2601 /* Notify application */
2602 COMMAND(SILC_STATUS_OK);
2604 /** Wait for command reply */
2605 silc_fsm_next(fsm, silc_client_command_reply_wait);
2606 return SILC_FSM_CONTINUE;
2609 return SILC_FSM_FINISH;
2612 /********************************* DETACH ***********************************/
2614 /* Command DETACH. This is used to detach from the server */
2616 SILC_FSM_STATE(silc_client_command_detach)
2618 SilcClientCommandContext cmd = fsm_context;
2619 SilcClientConnection conn = cmd->conn;
2621 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2623 /* Notify application */
2624 COMMAND(SILC_STATUS_OK);
2626 /** Wait for command reply */
2627 silc_fsm_next(fsm, silc_client_command_reply_wait);
2628 return SILC_FSM_CONTINUE;
2631 /********************************** WATCH ***********************************/
2633 /* Command WATCH. */
2635 SILC_FSM_STATE(silc_client_command_watch)
2637 SilcClientCommandContext cmd = fsm_context;
2638 SilcClientConnection conn = cmd->conn;
2639 SilcBuffer args = NULL;
2641 const char *pubkey = NULL;
2642 SilcBool pubkey_add = TRUE;
2644 if (cmd->argc < 3) {
2645 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2649 if (!strcasecmp(cmd->argv[1], "-add")) {
2651 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2653 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2655 pubkey = cmd->argv[2] + 1;
2656 if (cmd->argv[2][0] == '-')
2659 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2667 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2668 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2669 "Could not load public key %s, check the filename", pubkey);
2670 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2674 args = silc_buffer_alloc_size(2);
2675 silc_buffer_format(args,
2676 SILC_STR_UI_SHORT(1),
2678 buffer = silc_public_key_payload_encode(NULL, pk);
2679 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2680 pubkey_add ? 0x00 : 0x01);
2681 silc_buffer_free(buffer);
2682 silc_pkcs_public_key_free(pk);
2685 /* If watching by nickname, resolve all users with that nickname so that
2686 we get their information immediately. */
2688 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2689 silc_client_command_resolve_dummy, NULL);
2691 /* Send the commmand */
2692 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2693 1, silc_buffer_datalen(conn->internal->
2695 type, pubkey ? args->data : cmd->argv[2],
2696 pubkey ? silc_buffer_len(args) :
2699 silc_buffer_free(args);
2701 /* Notify application */
2702 COMMAND(SILC_STATUS_OK);
2704 /** Wait for command reply */
2705 silc_fsm_next(fsm, silc_client_command_reply_wait);
2706 return SILC_FSM_CONTINUE;
2709 return SILC_FSM_FINISH;
2712 /********************************** LEAVE ***********************************/
2714 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2716 SILC_FSM_STATE(silc_client_command_leave)
2718 SilcClientCommandContext cmd = fsm_context;
2719 SilcClientConnection conn = cmd->conn;
2720 SilcClient client = conn->client;
2721 SilcChannelEntry channel;
2723 char *name, tmp[512];
2725 if (cmd->argc != 2) {
2726 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2727 "Usage: /LEAVE <channel>");
2728 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2732 if (cmd->argv[1][0] == '*') {
2733 if (!conn->current_channel) {
2734 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2738 if (client->internal->params->full_channel_names)
2739 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2741 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2742 conn->current_channel->channel_name,
2743 conn->current_channel->server[0] ? "@" : "",
2744 conn->current_channel->server);
2747 name = cmd->argv[1];
2750 /* Get the channel entry */
2751 channel = silc_client_get_channel(conn->client, conn, name);
2753 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2757 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2759 /* Send LEAVE command to the server */
2760 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2761 1, silc_buffer_datalen(idp));
2763 silc_buffer_free(idp);
2765 /* Notify application */
2766 COMMAND(SILC_STATUS_OK);
2768 if (conn->current_channel == channel)
2769 conn->current_channel = NULL;
2771 silc_client_unref_channel(client, conn, channel);
2773 /** Wait for command reply */
2774 silc_fsm_next(fsm, silc_client_command_reply_wait);
2775 return SILC_FSM_CONTINUE;
2778 return SILC_FSM_FINISH;
2781 /********************************** USERS ***********************************/
2783 /* Command USERS. Requests the USERS of the clients joined on requested
2786 SILC_FSM_STATE(silc_client_command_users)
2788 SilcClientCommandContext cmd = fsm_context;
2789 SilcClientConnection conn = cmd->conn;
2790 char *name, tmp[512];
2792 if (cmd->argc != 2) {
2793 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2794 "Usage: /USERS <channel>");
2795 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2799 if (cmd->argv[1][0] == '*') {
2800 if (!conn->current_channel) {
2801 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2805 if (conn->client->internal->params->full_channel_names)
2806 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2808 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2809 conn->current_channel->channel_name,
2810 conn->current_channel->server[0] ? "@" : "",
2811 conn->current_channel->server);
2814 name = cmd->argv[1];
2817 /* Send USERS command to the server */
2818 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2819 2, name, strlen(name));
2821 /* Notify application */
2822 COMMAND(SILC_STATUS_OK);
2824 /** Wait for command reply */
2825 silc_fsm_next(fsm, silc_client_command_reply_wait);
2826 return SILC_FSM_CONTINUE;
2829 return SILC_FSM_FINISH;
2832 /********************************* GETKEY ***********************************/
2834 /* Command GETKEY. Used to fetch remote client's public key. */
2836 SILC_FSM_STATE(silc_client_command_getkey)
2838 SilcClientCommandContext cmd = fsm_context;
2839 SilcClientConnection conn = cmd->conn;
2840 SilcClient client = conn->client;
2841 SilcClientEntry client_entry;
2842 SilcServerEntry server_entry;
2846 if (cmd->argc < 2) {
2847 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2848 "Usage: /GETKEY <nickname or server name>");
2849 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2850 return SILC_FSM_FINISH;
2853 /* Find client entry */
2854 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2856 /* Check whether user requested server */
2857 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2858 if (!server_entry) {
2859 if (cmd->resolved) {
2860 /* Resolving didn't find anything. We should never get here as
2861 errors are handled in the resolving callback. */
2862 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2863 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2864 return SILC_FSM_FINISH;
2867 /* No client or server exist with this name, query for both. */
2868 cmd->resolved = TRUE;
2869 SILC_FSM_CALL(silc_client_command_send(client, conn,
2870 SILC_COMMAND_IDENTIFY,
2871 silc_client_command_continue,
2874 strlen(cmd->argv[1]),
2876 strlen(cmd->argv[1])));
2879 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2880 silc_client_unref_server(client, conn, server_entry);
2882 client_entry = silc_dlist_get(clients);
2883 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2884 silc_client_list_free(client, conn, clients);
2887 /* Send the commmand */
2888 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2889 1, silc_buffer_datalen(idp));
2891 silc_buffer_free(idp);
2893 /* Notify application */
2894 COMMAND(SILC_STATUS_OK);
2896 /** Wait for command reply */
2897 silc_fsm_next(fsm, silc_client_command_reply_wait);
2898 return SILC_FSM_CONTINUE;
2901 /********************************* SERVICE **********************************/
2903 /* Command SERVICE. Negotiates service agreement with server. */
2904 /* XXX incomplete */
2906 SILC_FSM_STATE(silc_client_command_service)
2908 SilcClientCommandContext cmd = fsm_context;
2910 SilcClientConnection conn = cmd->conn;
2914 if (cmd->argc < 2) {
2915 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2916 "Usage: /SERVICE [<service name>] [-pubkey]");
2917 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2918 return SILC_FSM_FINISH;
2921 name = cmd->argv[1];
2923 /* Send SERVICE command to the server */
2924 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2925 ++conn->cmd_ident, 1,
2926 1, name, strlen(name));
2927 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2928 NULL, 0, NULL, NULL, buffer->data,
2930 silc_buffer_free(buffer);
2933 /* Notify application */
2934 COMMAND(SILC_STATUS_OK);
2936 /** Wait for command reply */
2937 silc_fsm_next(fsm, silc_client_command_reply_wait);
2938 return SILC_FSM_CONTINUE;
2941 /* Register all default commands provided by the client library for the
2944 void silc_client_commands_register(SilcClient client)
2946 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2949 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2950 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2951 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2952 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2953 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2954 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2955 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2956 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2957 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2958 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2959 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2960 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2961 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2962 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2963 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2964 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2965 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2966 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2967 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2968 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2969 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2970 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2971 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2972 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2973 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2974 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2975 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2978 /* Unregister all commands. */
2980 void silc_client_commands_unregister(SilcClient client)
2982 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2983 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2984 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2985 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2986 SILC_CLIENT_CMDU(list, LIST, "LIST");
2987 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2988 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2989 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2990 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2991 SILC_CLIENT_CMDU(info, INFO, "INFO");
2992 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2993 SILC_CLIENT_CMDU(ping, PING, "PING");
2994 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2995 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2996 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2997 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2998 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2999 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3000 SILC_CLIENT_CMDU(kick, KICK, "KICK");
3001 SILC_CLIENT_CMDU(ban, BAN, "BAN");
3002 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3003 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3004 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3005 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3006 SILC_CLIENT_CMDU(users, USERS, "USERS");
3007 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3008 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3011 /****************** Client Side Incoming Command Handling *******************/
3014 SilcClientConnection conn;
3015 SilcUInt16 cmd_ident;
3016 } *SilcClientProcessWhois;
3018 /* Send reply to WHOIS from server */
3021 silc_client_command_process_whois_send(SilcBool success,
3022 const unsigned char *data,
3023 SilcUInt32 data_len, void *context)
3025 SilcClientProcessWhois w = context;
3026 SilcBufferStruct buffer;
3034 silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3036 /* Send the attributes back in COMMAND_REPLY packet */
3038 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3039 SILC_STATUS_OK, 0, w->cmd_ident,
3041 silc_buffer_len(&buffer));
3047 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3049 silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3050 silc_buffer_datalen(packet));
3052 silc_buffer_free(packet);
3056 /* Process WHOIS command from server */
3058 static void silc_client_command_process_whois(SilcClient client,
3059 SilcClientConnection conn,
3060 SilcCommandPayload payload,
3061 SilcArgumentPayload args)
3063 SilcClientProcessWhois w;
3068 SILC_LOG_DEBUG(("Received WHOIS command"));
3070 /* Try to take the Requested Attributes */
3071 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3075 attrs = silc_attribute_payload_parse(tmp, tmp_len);
3079 w = silc_calloc(1, sizeof(*w));
3081 silc_attribute_payload_list_free(attrs);
3085 w->cmd_ident = silc_command_get_ident(payload);
3087 /* Process requested attributes */
3088 silc_client_attributes_process(client, conn, attrs,
3089 silc_client_command_process_whois_send, w);
3090 silc_attribute_payload_list_free(attrs);
3093 /* Client is able to receive some command packets even though they are
3094 special case. Server may send WHOIS command to the client to retrieve
3095 Requested Attributes information for WHOIS query the server is
3096 processing. This function currently handles only the WHOIS command,
3097 but if in the future more commands may arrive then this can be made
3098 to support other commands too. */
3100 SILC_FSM_STATE(silc_client_command)
3102 SilcClientConnection conn = fsm_context;
3103 SilcClient client = conn->client;
3104 SilcPacket packet = state_context;
3105 SilcCommandPayload payload;
3106 SilcCommand command;
3107 SilcArgumentPayload args;
3109 /* Get command payload from packet */
3110 payload = silc_command_payload_parse(packet->buffer.data,
3111 silc_buffer_len(&packet->buffer));
3113 SILC_LOG_DEBUG(("Bad command packet"));
3114 return SILC_FSM_FINISH;
3118 args = silc_command_get_args(payload);
3120 /* Get the command */
3121 command = silc_command_get(payload);
3124 case SILC_COMMAND_WHOIS:
3125 /* Ignore everything if requested by application */
3126 if (conn->internal->params.ignore_requested_attributes)
3129 silc_client_command_process_whois(client, conn, payload, args);
3136 silc_command_payload_free(payload);
3137 return SILC_FSM_FINISH;