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, SILC_PKCS_ANY, &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, SILC_PKCS_ANY, &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,
1507 silc_client_command_join_signed,
1512 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1513 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1514 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1515 cmd->argv_lens[i], 0);
1516 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1517 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1518 0, pu8, passphrase_len);
1521 passphrase = strdup(cmd->argv[i]);
1522 passphrase_len = cmd->argv_lens[i];
1527 /* Send JOIN command to the server */
1528 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1529 1, name, strlen(name),
1530 2, silc_buffer_datalen(conn->internal->
1532 3, passphrase, passphrase_len,
1533 4, cipher, cipher ? strlen(cipher) : 0,
1534 5, hmac, hmac ? strlen(hmac) : 0,
1535 6, silc_buffer_datalen(auth),
1536 7, silc_buffer_datalen(cauth));
1539 memset(passphrase, 0, strlen(passphrase));
1540 silc_free(passphrase);
1541 silc_client_unref_channel(client, conn, channel);
1543 silc_buffer_free(j->auth);
1544 silc_buffer_free(j->cauth);
1548 /* Notify application */
1549 COMMAND(SILC_STATUS_OK);
1551 /** Wait for command reply */
1552 silc_fsm_next(fsm, silc_client_command_reply_wait);
1553 return SILC_FSM_CONTINUE;
1556 silc_client_unref_channel(client, conn, channel);
1557 return SILC_FSM_FINISH;
1560 /********************************** MOTD ************************************/
1562 /* MOTD command. Requests motd from server. */
1564 SILC_FSM_STATE(silc_client_command_motd)
1566 SilcClientCommandContext cmd = fsm_context;
1567 SilcClientConnection conn = cmd->conn;
1569 if (cmd->argc < 1 || cmd->argc > 2) {
1570 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1571 "Usage: /MOTD [<server>]");
1572 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1573 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1574 return SILC_FSM_FINISH;
1577 /* Send the command */
1579 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1580 1, conn->remote_host,
1581 strlen(conn->remote_host));
1583 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1584 1, cmd->argv[1], cmd->argv_lens[1]);
1586 /* Notify application */
1587 COMMAND(SILC_STATUS_OK);
1589 /** Wait for command reply */
1590 silc_fsm_next(fsm, silc_client_command_reply_wait);
1591 return SILC_FSM_CONTINUE;
1594 /********************************** UMODE ***********************************/
1596 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1597 modes as client cannot set itself server/router operator privileges. */
1599 SILC_FSM_STATE(silc_client_command_umode)
1601 SilcClientCommandContext cmd = fsm_context;
1602 SilcClientConnection conn = cmd->conn;
1603 unsigned char *cp, modebuf[4];
1604 SilcUInt32 mode, add, len;
1607 if (cmd->argc < 2) {
1608 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1609 "Usage: /UMODE +|-<modes>");
1610 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1611 return SILC_FSM_FINISH;
1614 mode = conn->local_entry->mode;
1616 /* Are we adding or removing mode */
1617 if (cmd->argv[1][0] == '-')
1623 cp = cmd->argv[1] + 1;
1625 for (i = 0; i < len; i++) {
1630 mode |= SILC_UMODE_SERVER_OPERATOR;
1631 mode |= SILC_UMODE_ROUTER_OPERATOR;
1632 mode |= SILC_UMODE_GONE;
1633 mode |= SILC_UMODE_INDISPOSED;
1634 mode |= SILC_UMODE_BUSY;
1635 mode |= SILC_UMODE_PAGE;
1636 mode |= SILC_UMODE_HYPER;
1637 mode |= SILC_UMODE_ROBOT;
1638 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1639 mode |= SILC_UMODE_REJECT_WATCHING;
1641 mode = SILC_UMODE_NONE;
1646 mode |= SILC_UMODE_SERVER_OPERATOR;
1648 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1652 mode |= SILC_UMODE_ROUTER_OPERATOR;
1654 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1658 mode |= SILC_UMODE_GONE;
1660 mode &= ~SILC_UMODE_GONE;
1664 mode |= SILC_UMODE_INDISPOSED;
1666 mode &= ~SILC_UMODE_INDISPOSED;
1670 mode |= SILC_UMODE_BUSY;
1672 mode &= ~SILC_UMODE_BUSY;
1676 mode |= SILC_UMODE_PAGE;
1678 mode &= ~SILC_UMODE_PAGE;
1682 mode |= SILC_UMODE_HYPER;
1684 mode &= ~SILC_UMODE_HYPER;
1688 mode |= SILC_UMODE_ROBOT;
1690 mode &= ~SILC_UMODE_ROBOT;
1694 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1696 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1700 mode |= SILC_UMODE_REJECT_WATCHING;
1702 mode &= ~SILC_UMODE_REJECT_WATCHING;
1706 mode |= SILC_UMODE_BLOCK_INVITE;
1708 mode &= ~SILC_UMODE_BLOCK_INVITE;
1711 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1712 return SILC_FSM_FINISH;
1717 SILC_PUT32_MSB(mode, modebuf);
1719 /* Send the command */
1720 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1721 1, silc_buffer_datalen(conn->internal->
1723 2, modebuf, sizeof(modebuf));
1725 /* Notify application */
1726 COMMAND(SILC_STATUS_OK);
1728 /** Wait for command reply */
1729 silc_fsm_next(fsm, silc_client_command_reply_wait);
1730 return SILC_FSM_CONTINUE;
1733 /********************************** CMODE ***********************************/
1735 /* Signature callback */
1737 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1740 SilcClientCommandContext cmd = context;
1743 silc_fsm_finish(&cmd->thread);
1747 silc_fsm_set_state_context(&cmd->thread, buffer);
1748 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1751 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1752 can be set several at once. Those modes that require argument must be set
1753 separately (unless set with modes that does not require arguments). */
1755 SILC_FSM_STATE(silc_client_command_cmode)
1757 SilcClientCommandContext cmd = fsm_context;
1758 SilcClientConnection conn = cmd->conn;
1759 SilcClient client = conn->client;
1760 SilcBuffer auth = state_context;
1761 SilcChannelEntry channel = NULL;
1762 SilcBuffer chidp, pk = NULL;
1763 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1764 SilcUInt32 mode, add, type, len, arg_len = 0;
1767 if (cmd->argc < 3) {
1768 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1769 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1770 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1774 if (cmd->argv[1][0] == '*') {
1775 if (!conn->current_channel) {
1776 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1780 channel = conn->current_channel;
1781 silc_client_ref_channel(client, conn, channel);
1783 name = cmd->argv[1];
1785 channel = silc_client_get_channel(conn->client, conn, name);
1787 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1792 mode = channel->mode;
1794 /* Are we adding or removing mode */
1795 if (cmd->argv[2][0] == '-')
1800 /* Argument type to be sent to server */
1804 cp = cmd->argv[2] + 1;
1806 for (i = 0; i < len; i++) {
1810 mode |= SILC_CHANNEL_MODE_PRIVATE;
1812 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1816 mode |= SILC_CHANNEL_MODE_SECRET;
1818 mode &= ~SILC_CHANNEL_MODE_SECRET;
1822 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1824 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1828 mode |= SILC_CHANNEL_MODE_INVITE;
1830 mode &= ~SILC_CHANNEL_MODE_INVITE;
1834 mode |= SILC_CHANNEL_MODE_TOPIC;
1836 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1840 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1842 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1846 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1848 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1853 mode |= SILC_CHANNEL_MODE_ULIMIT;
1855 if (cmd->argc < 4) {
1856 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1857 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1858 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1861 ll = atoi(cmd->argv[3]);
1862 SILC_PUT32_MSB(ll, tmp);
1866 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1871 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1873 if (cmd->argc < 4) {
1874 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1875 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1876 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1880 arg_len = cmd->argv_lens[3];
1882 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1887 mode |= SILC_CHANNEL_MODE_CIPHER;
1889 if (cmd->argc < 4) {
1890 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1891 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1892 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1896 arg_len = cmd->argv_lens[3];
1898 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1903 mode |= SILC_CHANNEL_MODE_HMAC;
1905 if (cmd->argc < 4) {
1906 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1907 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1908 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1912 arg_len = cmd->argv_lens[3];
1914 mode &= ~SILC_CHANNEL_MODE_HMAC;
1920 SilcPublicKey pubkey = conn->public_key;
1921 SilcPrivateKey privkey = conn->private_key;
1923 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1926 if (cmd->argc >= 5) {
1929 pass = cmd->argv[5];
1930 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1931 &pubkey, &privkey)) {
1932 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1933 "Could not load key pair, check your arguments");
1934 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1939 pk = silc_public_key_payload_encode(NULL, pubkey);
1940 silc_client_unref_channel(client, conn, channel);
1941 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1944 conn->internal->sha1hash,
1947 silc_client_command_cmode_signed,
1951 arg = silc_buffer_data(auth);
1952 arg_len = silc_buffer_len(auth);
1954 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1960 SilcBool chadd = FALSE;
1961 SilcPublicKey chpk = NULL;
1963 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1966 if (cmd->argc == 3) {
1967 /* Send empty command to receive the public key list. */
1968 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1969 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1971 1, silc_buffer_datalen(chidp));
1972 silc_buffer_free(chidp);
1973 silc_client_unref_channel(client, conn, channel);
1975 /* Notify application */
1976 COMMAND(SILC_STATUS_OK);
1978 /** Wait for command reply */
1979 silc_fsm_next(fsm, silc_client_command_reply_wait);
1980 return SILC_FSM_CONTINUE;
1983 if (cmd->argc >= 4) {
1984 auth = silc_buffer_alloc_size(2);
1985 silc_buffer_format(auth,
1986 SILC_STR_UI_SHORT(cmd->argc - 3),
1990 for (k = 3; k < cmd->argc; k++) {
1991 if (cmd->argv[k][0] == '+')
1993 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, SILC_PKCS_ANY,
1995 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1996 "Could not load public key %s, check the filename",
1998 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1999 silc_buffer_free(auth);
2004 pk = silc_public_key_payload_encode(NULL, chpk);
2005 auth = silc_argument_payload_encode_one(auth,
2006 silc_buffer_datalen(pk),
2007 chadd ? 0x00 : 0x01);
2008 silc_pkcs_public_key_free(chpk);
2009 silc_buffer_free(pk);
2014 arg = silc_buffer_data(auth);
2015 arg_len = silc_buffer_len(auth);
2017 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2021 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2027 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2028 SILC_PUT32_MSB(mode, modebuf);
2030 /* Send the command. We support sending only one mode at once that
2031 requires an argument. */
2033 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2034 1, silc_buffer_datalen(chidp),
2035 2, modebuf, sizeof(modebuf),
2037 8, silc_buffer_datalen(pk));
2039 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2040 1, silc_buffer_datalen(chidp),
2041 2, modebuf, sizeof(modebuf));
2044 silc_buffer_free(chidp);
2045 silc_buffer_free(auth);
2046 silc_buffer_free(pk);
2047 silc_client_unref_channel(client, conn, channel);
2049 /* Notify application */
2050 COMMAND(SILC_STATUS_OK);
2052 /** Wait for command reply */
2053 silc_fsm_next(fsm, silc_client_command_reply_wait);
2054 return SILC_FSM_CONTINUE;
2057 silc_client_unref_channel(client, conn, channel);
2058 return SILC_FSM_FINISH;
2061 /********************************* CUMODE ***********************************/
2063 /* Signature callback */
2065 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2068 SilcClientCommandContext cmd = context;
2071 silc_fsm_finish(&cmd->thread);
2075 silc_fsm_set_state_context(&cmd->thread, buffer);
2076 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2079 /* CUMODE command. Changes client's mode on a channel. */
2081 SILC_FSM_STATE(silc_client_command_cumode)
2083 SilcClientCommandContext cmd = fsm_context;
2084 SilcClientConnection conn = cmd->conn;
2085 SilcClient client = conn->client;
2086 SilcBuffer auth = state_context;
2087 SilcChannelEntry channel = NULL;
2088 SilcChannelUser chu;
2089 SilcClientEntry client_entry;
2090 SilcBuffer clidp, chidp;
2091 SilcDList clients = NULL;
2092 unsigned char *name, *cp, modebuf[4];
2093 SilcUInt32 mode = 0, add, len;
2094 char *nickname = NULL;
2097 if (cmd->argc < 4) {
2098 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2099 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2100 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2104 if (cmd->argv[1][0] == '*') {
2105 if (!conn->current_channel) {
2106 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2110 channel = conn->current_channel;
2111 silc_client_ref_channel(client, conn, channel);
2113 name = cmd->argv[1];
2115 channel = silc_client_get_channel(conn->client, conn, name);
2117 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2122 /* Parse the typed nickname. */
2123 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
2125 /* Find client entry */
2126 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2128 /* Resolve client information */
2129 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2130 silc_client_command_resolve_continue,
2133 client_entry = silc_dlist_get(clients);
2135 /* Get the current mode */
2136 chu = silc_client_on_channel(channel, client_entry);
2140 /* Are we adding or removing mode */
2141 if (cmd->argv[2][0] == '-')
2147 cp = cmd->argv[2] + 1;
2149 for (i = 0; i < len; i++) {
2153 mode |= SILC_CHANNEL_UMODE_CHANFO;
2154 mode |= SILC_CHANNEL_UMODE_CHANOP;
2155 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2156 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2157 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2159 mode = SILC_CHANNEL_UMODE_NONE;
2165 SilcPublicKey pubkey = conn->public_key;
2166 SilcPrivateKey privkey = conn->private_key;
2168 if (cmd->argc >= 6) {
2171 pass = cmd->argv[6];
2172 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2173 &pubkey, &privkey)) {
2174 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2175 "Could not load key pair, check your arguments");
2176 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2181 silc_free(nickname);
2182 silc_client_list_free(client, conn, clients);
2183 silc_client_unref_channel(client, conn, channel);
2185 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2188 conn->internal->sha1hash,
2191 silc_client_command_cumode_signed,
2196 mode |= SILC_CHANNEL_UMODE_CHANFO;
2198 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2203 mode |= SILC_CHANNEL_UMODE_CHANOP;
2205 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2209 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2211 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2215 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2217 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2221 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2223 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2227 mode |= SILC_CHANNEL_UMODE_QUIET;
2229 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2232 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2238 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2239 SILC_PUT32_MSB(mode, modebuf);
2240 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2242 /* Send the command packet. We support sending only one mode at once
2243 that requires an argument. */
2244 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2245 1, silc_buffer_datalen(chidp),
2247 3, silc_buffer_datalen(clidp),
2248 4, silc_buffer_datalen(auth));
2250 silc_buffer_free(chidp);
2251 silc_buffer_free(clidp);
2253 silc_buffer_free(auth);
2254 silc_free(nickname);
2255 silc_client_list_free(client, conn, clients);
2256 silc_client_unref_channel(client, conn, channel);
2258 /* Notify application */
2259 COMMAND(SILC_STATUS_OK);
2261 /** Wait for command reply */
2262 silc_fsm_next(fsm, silc_client_command_reply_wait);
2263 return SILC_FSM_CONTINUE;
2266 silc_client_unref_channel(client, conn, channel);
2267 silc_client_list_free(client, conn, clients);
2268 silc_free(nickname);
2269 return SILC_FSM_FINISH;
2272 /********************************** KICK ************************************/
2274 /* KICK command. Kicks a client out of channel. */
2276 SILC_FSM_STATE(silc_client_command_kick)
2278 SilcClientCommandContext cmd = fsm_context;
2279 SilcClientConnection conn = cmd->conn;
2280 SilcClient client = conn->client;
2281 SilcChannelEntry channel = NULL;
2282 SilcBuffer idp, idp2;
2283 SilcClientEntry target;
2284 SilcDList clients = NULL;
2285 char *name, tmp[512];
2287 if (cmd->argc < 3) {
2288 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2289 "Usage: /KICK <channel> <nickname> [<comment>]");
2290 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2294 if (cmd->argv[1][0] == '*') {
2295 if (!conn->current_channel) {
2296 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2300 if (client->internal->params->full_channel_names)
2301 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2303 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2304 conn->current_channel->channel_name,
2305 conn->current_channel->server[0] ? "@" : "",
2306 conn->current_channel->server);
2309 name = cmd->argv[1];
2312 if (!conn->current_channel) {
2313 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2317 /* Get the Channel ID of the channel */
2318 channel = silc_client_get_channel(conn->client, conn, name);
2320 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2324 /* Get the target client */
2325 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2327 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2328 "No such client: %s", cmd->argv[2]);
2329 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2332 target = silc_dlist_get(clients);
2334 /* Send KICK command to the server */
2335 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2336 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2338 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2339 1, silc_buffer_datalen(idp),
2340 2, silc_buffer_datalen(idp2));
2342 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2343 1, silc_buffer_datalen(idp),
2344 2, silc_buffer_datalen(idp2),
2345 3, cmd->argv[3], strlen(cmd->argv[3]));
2347 silc_buffer_free(idp);
2348 silc_buffer_free(idp2);
2349 silc_client_list_free(client, conn, clients);
2350 silc_client_unref_channel(client, conn, channel);
2352 /* Notify application */
2353 COMMAND(SILC_STATUS_OK);
2355 /** Wait for command reply */
2356 silc_fsm_next(fsm, silc_client_command_reply_wait);
2357 return SILC_FSM_CONTINUE;
2360 silc_client_unref_channel(client, conn, channel);
2361 return SILC_FSM_FINISH;
2364 /***************************** OPER & SILCOPER ******************************/
2367 unsigned char *passphrase;
2368 SilcUInt32 passphrase_len;
2370 } *SilcClientCommandOper;
2372 /* Ask passphrase callback */
2374 static void silc_client_command_oper_cb(const unsigned char *data,
2375 SilcUInt32 data_len, void *context)
2377 SilcClientCommandContext cmd = context;
2378 SilcClientCommandOper oper = cmd->context;
2380 if (data && data_len)
2381 oper->passphrase = silc_memdup(data, data_len);
2382 oper->passphrase_len = data_len;
2385 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2388 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2391 SilcClientCommandContext cmd = context;
2392 SilcClientCommandOper oper = cmd->context;
2395 oper->auth = silc_buffer_copy(data);
2398 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2401 /* Send OPER/SILCOPER command */
2403 SILC_FSM_STATE(silc_client_command_oper_send)
2405 SilcClientCommandContext cmd = fsm_context;
2406 SilcClientConnection conn = cmd->conn;
2407 SilcClientCommandOper oper = cmd->context;
2408 SilcBuffer auth = oper ? oper->auth : NULL;
2410 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2411 1, cmd->argv[1], strlen(cmd->argv[1]),
2412 2, silc_buffer_datalen(auth));
2414 silc_buffer_clear(auth);
2415 silc_buffer_free(auth);
2417 silc_free(oper->passphrase);
2421 /* Notify application */
2422 COMMAND(SILC_STATUS_OK);
2424 /** Wait for command reply */
2425 silc_fsm_next(fsm, silc_client_command_reply_wait);
2426 return SILC_FSM_CONTINUE;
2429 /* Get authentication data */
2431 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2433 SilcClientCommandContext cmd = fsm_context;
2434 SilcClientConnection conn = cmd->conn;
2435 SilcClientCommandOper oper = cmd->context;
2437 silc_fsm_next(fsm, silc_client_command_oper_send);
2439 if (!oper || !oper->passphrase) {
2440 /* Encode the public key authentication payload */
2441 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2445 conn->internal->hash,
2446 conn->local_id, SILC_ID_CLIENT,
2447 silc_client_command_oper_sign_cb,
2452 /* Encode the password authentication payload */
2453 oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2454 oper->passphrase, oper->passphrase_len);
2456 return SILC_FSM_CONTINUE;
2459 /* OPER command. Used to obtain server operator privileges. */
2461 SILC_FSM_STATE(silc_client_command_oper)
2463 SilcClientCommandContext cmd = fsm_context;
2464 SilcClientConnection conn = cmd->conn;
2465 SilcClientCommandOper oper;
2467 if (cmd->argc < 2) {
2468 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2469 "Usage: /OPER <username> [-pubkey]");
2470 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2471 return SILC_FSM_FINISH;
2474 silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2476 /* Get passphrase */
2477 if (cmd->argc < 3) {
2478 oper = silc_calloc(1, sizeof(*oper));
2480 return SILC_FSM_FINISH;
2481 cmd->context = oper;
2482 SILC_FSM_CALL(conn->client->internal->
2483 ops->ask_passphrase(conn->client, conn,
2484 silc_client_command_oper_cb, cmd));
2487 return SILC_FSM_CONTINUE;
2490 /* SILCOPER command. Used to obtain router operator privileges. */
2492 SILC_FSM_STATE(silc_client_command_silcoper)
2494 SilcClientCommandContext cmd = fsm_context;
2495 SilcClientConnection conn = cmd->conn;
2496 SilcClientCommandOper oper;
2498 if (cmd->argc < 2) {
2499 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2500 "Usage: /SILCOPER <username> [-pubkey]");
2501 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2502 return SILC_FSM_FINISH;
2505 silc_fsm_next(fsm, silc_client_command_oper_send);
2507 /* Get passphrase */
2508 if (cmd->argc < 3) {
2509 oper = silc_calloc(1, sizeof(*oper));
2511 return SILC_FSM_FINISH;
2512 cmd->context = oper;
2513 SILC_FSM_CALL(conn->client->internal->
2514 ops->ask_passphrase(conn->client, conn,
2515 silc_client_command_oper_cb, cmd));
2518 return SILC_FSM_CONTINUE;
2521 /*********************************** BAN ************************************/
2523 /* Command BAN. This is used to manage the ban list of the channel. */
2525 SILC_FSM_STATE(silc_client_command_ban)
2527 SilcClientCommandContext cmd = fsm_context;
2528 SilcClientConnection conn = cmd->conn;
2529 SilcClient client = conn->client;
2530 SilcChannelEntry channel;
2531 SilcBuffer chidp, args = NULL;
2532 char *name, *ban = NULL;
2533 unsigned char action[1];
2534 SilcPublicKey pubkey = NULL;
2536 if (cmd->argc < 2) {
2537 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2538 "Usage: /BAN <channel> "
2539 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2540 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2544 if (cmd->argv[1][0] == '*') {
2545 if (!conn->current_channel) {
2546 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2550 channel = conn->current_channel;
2551 silc_client_ref_channel(client, conn, channel);
2553 name = cmd->argv[1];
2555 channel = silc_client_get_channel(conn->client, conn, name);
2557 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2562 if (cmd->argc == 3) {
2563 if (cmd->argv[2][0] == '+')
2568 /* Check if it is public key file to be added to invite list */
2569 silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
2576 args = silc_buffer_alloc_size(2);
2577 silc_buffer_format(args,
2578 SILC_STR_UI_SHORT(1),
2581 chidp = silc_public_key_payload_encode(NULL, pubkey);
2582 args = silc_argument_payload_encode_one(args,
2583 silc_buffer_datalen(chidp), 2);
2584 silc_buffer_free(chidp);
2585 silc_pkcs_public_key_free(pubkey);
2587 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2591 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2593 /* Send the command */
2594 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2595 1, silc_buffer_datalen(chidp),
2596 2, args ? action : NULL, args ? 1 : 0,
2597 3, silc_buffer_datalen(args));
2599 silc_buffer_free(chidp);
2600 silc_buffer_free(args);
2601 silc_client_unref_channel(client, conn, channel);
2603 /* Notify application */
2604 COMMAND(SILC_STATUS_OK);
2606 /** Wait for command reply */
2607 silc_fsm_next(fsm, silc_client_command_reply_wait);
2608 return SILC_FSM_CONTINUE;
2611 return SILC_FSM_FINISH;
2614 /********************************* DETACH ***********************************/
2616 /* Command DETACH. This is used to detach from the server */
2618 SILC_FSM_STATE(silc_client_command_detach)
2620 SilcClientCommandContext cmd = fsm_context;
2621 SilcClientConnection conn = cmd->conn;
2623 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2625 /* Notify application */
2626 COMMAND(SILC_STATUS_OK);
2628 /** Wait for command reply */
2629 silc_fsm_next(fsm, silc_client_command_reply_wait);
2630 return SILC_FSM_CONTINUE;
2633 /********************************** WATCH ***********************************/
2635 /* Command WATCH. */
2637 SILC_FSM_STATE(silc_client_command_watch)
2639 SilcClientCommandContext cmd = fsm_context;
2640 SilcClientConnection conn = cmd->conn;
2641 SilcBuffer args = NULL;
2643 const char *pubkey = NULL;
2644 SilcBool pubkey_add = TRUE;
2646 if (cmd->argc < 3) {
2647 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2651 if (!strcasecmp(cmd->argv[1], "-add")) {
2653 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2655 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2657 pubkey = cmd->argv[2] + 1;
2658 if (cmd->argv[2][0] == '-')
2661 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2669 if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
2670 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2671 "Could not load public key %s, check the filename", pubkey);
2672 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2676 args = silc_buffer_alloc_size(2);
2677 silc_buffer_format(args,
2678 SILC_STR_UI_SHORT(1),
2680 buffer = silc_public_key_payload_encode(NULL, pk);
2681 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2682 pubkey_add ? 0x00 : 0x01);
2683 silc_buffer_free(buffer);
2684 silc_pkcs_public_key_free(pk);
2687 /* If watching by nickname, resolve all users with that nickname so that
2688 we get their information immediately. */
2690 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2691 silc_client_command_resolve_dummy, NULL);
2693 /* Send the commmand */
2694 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2695 1, silc_buffer_datalen(conn->internal->
2697 type, pubkey ? args->data : cmd->argv[2],
2698 pubkey ? silc_buffer_len(args) :
2701 silc_buffer_free(args);
2703 /* Notify application */
2704 COMMAND(SILC_STATUS_OK);
2706 /** Wait for command reply */
2707 silc_fsm_next(fsm, silc_client_command_reply_wait);
2708 return SILC_FSM_CONTINUE;
2711 return SILC_FSM_FINISH;
2714 /********************************** LEAVE ***********************************/
2716 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2718 SILC_FSM_STATE(silc_client_command_leave)
2720 SilcClientCommandContext cmd = fsm_context;
2721 SilcClientConnection conn = cmd->conn;
2722 SilcClient client = conn->client;
2723 SilcChannelEntry channel;
2725 char *name, tmp[512];
2727 if (cmd->argc != 2) {
2728 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2729 "Usage: /LEAVE <channel>");
2730 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2734 if (cmd->argv[1][0] == '*') {
2735 if (!conn->current_channel) {
2736 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2740 if (client->internal->params->full_channel_names)
2741 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2743 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2744 conn->current_channel->channel_name,
2745 conn->current_channel->server[0] ? "@" : "",
2746 conn->current_channel->server);
2749 name = cmd->argv[1];
2752 /* Get the channel entry */
2753 channel = silc_client_get_channel(conn->client, conn, name);
2755 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2759 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2761 /* Send LEAVE command to the server */
2762 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2763 1, silc_buffer_datalen(idp));
2765 silc_buffer_free(idp);
2767 /* Notify application */
2768 COMMAND(SILC_STATUS_OK);
2770 if (conn->current_channel == channel)
2771 conn->current_channel = NULL;
2773 silc_client_unref_channel(client, conn, channel);
2775 /** Wait for command reply */
2776 silc_fsm_next(fsm, silc_client_command_reply_wait);
2777 return SILC_FSM_CONTINUE;
2780 return SILC_FSM_FINISH;
2783 /********************************** USERS ***********************************/
2785 /* Command USERS. Requests the USERS of the clients joined on requested
2788 SILC_FSM_STATE(silc_client_command_users)
2790 SilcClientCommandContext cmd = fsm_context;
2791 SilcClientConnection conn = cmd->conn;
2792 char *name, tmp[512];
2794 if (cmd->argc != 2) {
2795 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2796 "Usage: /USERS <channel>");
2797 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2801 if (cmd->argv[1][0] == '*') {
2802 if (!conn->current_channel) {
2803 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2807 if (conn->client->internal->params->full_channel_names)
2808 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2810 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2811 conn->current_channel->channel_name,
2812 conn->current_channel->server[0] ? "@" : "",
2813 conn->current_channel->server);
2816 name = cmd->argv[1];
2819 /* Send USERS command to the server */
2820 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2821 2, name, strlen(name));
2823 /* Notify application */
2824 COMMAND(SILC_STATUS_OK);
2826 /** Wait for command reply */
2827 silc_fsm_next(fsm, silc_client_command_reply_wait);
2828 return SILC_FSM_CONTINUE;
2831 return SILC_FSM_FINISH;
2834 /********************************* GETKEY ***********************************/
2836 /* Command GETKEY. Used to fetch remote client's public key. */
2838 SILC_FSM_STATE(silc_client_command_getkey)
2840 SilcClientCommandContext cmd = fsm_context;
2841 SilcClientConnection conn = cmd->conn;
2842 SilcClient client = conn->client;
2843 SilcClientEntry client_entry;
2844 SilcServerEntry server_entry;
2848 if (cmd->argc < 2) {
2849 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2850 "Usage: /GETKEY <nickname or server name>");
2851 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2852 return SILC_FSM_FINISH;
2855 /* Find client entry */
2856 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2858 /* Check whether user requested server */
2859 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2860 if (!server_entry) {
2861 if (cmd->resolved) {
2862 /* Resolving didn't find anything. We should never get here as
2863 errors are handled in the resolving callback. */
2864 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2865 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2866 return SILC_FSM_FINISH;
2869 /* No client or server exist with this name, query for both. */
2870 cmd->resolved = TRUE;
2871 SILC_FSM_CALL(silc_client_command_send(client, conn,
2872 SILC_COMMAND_IDENTIFY,
2873 silc_client_command_continue,
2876 strlen(cmd->argv[1]),
2878 strlen(cmd->argv[1])));
2881 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2882 silc_client_unref_server(client, conn, server_entry);
2884 client_entry = silc_dlist_get(clients);
2885 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2886 silc_client_list_free(client, conn, clients);
2889 /* Send the commmand */
2890 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2891 1, silc_buffer_datalen(idp));
2893 silc_buffer_free(idp);
2895 /* Notify application */
2896 COMMAND(SILC_STATUS_OK);
2898 /** Wait for command reply */
2899 silc_fsm_next(fsm, silc_client_command_reply_wait);
2900 return SILC_FSM_CONTINUE;
2903 /********************************* SERVICE **********************************/
2905 /* Command SERVICE. Negotiates service agreement with server. */
2906 /* XXX incomplete */
2908 SILC_FSM_STATE(silc_client_command_service)
2910 SilcClientCommandContext cmd = fsm_context;
2912 SilcClientConnection conn = cmd->conn;
2916 if (cmd->argc < 2) {
2917 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2918 "Usage: /SERVICE [<service name>] [-pubkey]");
2919 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2920 return SILC_FSM_FINISH;
2923 name = cmd->argv[1];
2925 /* Send SERVICE command to the server */
2926 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2927 ++conn->cmd_ident, 1,
2928 1, name, strlen(name));
2929 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2930 NULL, 0, NULL, NULL, buffer->data,
2932 silc_buffer_free(buffer);
2935 /* Notify application */
2936 COMMAND(SILC_STATUS_OK);
2938 /** Wait for command reply */
2939 silc_fsm_next(fsm, silc_client_command_reply_wait);
2940 return SILC_FSM_CONTINUE;
2943 /* Register all default commands provided by the client library for the
2946 void silc_client_commands_register(SilcClient client)
2948 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2951 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2952 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2953 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2954 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2955 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2956 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2957 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2958 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2959 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2960 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2961 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2962 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2963 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2964 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2965 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2966 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2967 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2968 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2969 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2970 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2971 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2972 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2973 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2974 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2975 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2976 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2977 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2980 /* Unregister all commands. */
2982 void silc_client_commands_unregister(SilcClient client)
2984 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2985 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2986 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2987 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2988 SILC_CLIENT_CMDU(list, LIST, "LIST");
2989 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2990 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2991 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2992 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2993 SILC_CLIENT_CMDU(info, INFO, "INFO");
2994 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2995 SILC_CLIENT_CMDU(ping, PING, "PING");
2996 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2997 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2998 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2999 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
3000 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
3001 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3002 SILC_CLIENT_CMDU(kick, KICK, "KICK");
3003 SILC_CLIENT_CMDU(ban, BAN, "BAN");
3004 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3005 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3006 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3007 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3008 SILC_CLIENT_CMDU(users, USERS, "USERS");
3009 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3010 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3013 /****************** Client Side Incoming Command Handling *******************/
3016 SilcClientConnection conn;
3017 SilcUInt16 cmd_ident;
3018 } *SilcClientProcessWhois;
3020 /* Send reply to WHOIS from server */
3023 silc_client_command_process_whois_send(SilcBool success,
3024 const unsigned char *data,
3025 SilcUInt32 data_len, void *context)
3027 SilcClientProcessWhois w = context;
3028 SilcBufferStruct buffer;
3036 silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3038 /* Send the attributes back in COMMAND_REPLY packet */
3040 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3041 SILC_STATUS_OK, 0, w->cmd_ident,
3043 silc_buffer_len(&buffer));
3049 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3051 silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3052 silc_buffer_datalen(packet));
3054 silc_buffer_free(packet);
3058 /* Process WHOIS command from server */
3060 static void silc_client_command_process_whois(SilcClient client,
3061 SilcClientConnection conn,
3062 SilcCommandPayload payload,
3063 SilcArgumentPayload args)
3065 SilcClientProcessWhois w;
3070 SILC_LOG_DEBUG(("Received WHOIS command"));
3072 /* Try to take the Requested Attributes */
3073 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3077 attrs = silc_attribute_payload_parse(tmp, tmp_len);
3081 w = silc_calloc(1, sizeof(*w));
3083 silc_attribute_payload_list_free(attrs);
3087 w->cmd_ident = silc_command_get_ident(payload);
3089 /* Process requested attributes */
3090 silc_client_attributes_process(client, conn, attrs,
3091 silc_client_command_process_whois_send, w);
3092 silc_attribute_payload_list_free(attrs);
3095 /* Client is able to receive some command packets even though they are
3096 special case. Server may send WHOIS command to the client to retrieve
3097 Requested Attributes information for WHOIS query the server is
3098 processing. This function currently handles only the WHOIS command,
3099 but if in the future more commands may arrive then this can be made
3100 to support other commands too. */
3102 SILC_FSM_STATE(silc_client_command)
3104 SilcClientConnection conn = fsm_context;
3105 SilcClient client = conn->client;
3106 SilcPacket packet = state_context;
3107 SilcCommandPayload payload;
3108 SilcCommand command;
3109 SilcArgumentPayload args;
3111 /* Get command payload from packet */
3112 payload = silc_command_payload_parse(packet->buffer.data,
3113 silc_buffer_len(&packet->buffer));
3115 SILC_LOG_DEBUG(("Bad command packet"));
3116 return SILC_FSM_FINISH;
3120 args = silc_command_get_args(payload);
3122 /* Get the command */
3123 command = silc_command_get(payload);
3126 case SILC_COMMAND_WHOIS:
3127 /* Ignore everything if requested by application */
3128 if (conn->internal->params.ignore_requested_attributes)
3131 silc_client_command_process_whois(client, conn, payload, args);
3138 silc_command_payload_free(payload);
3139 return SILC_FSM_FINISH;