5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 /* Command operation that is called at the end of all commands.
28 Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command( \
30 cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status) \
34 cmd->conn->client->internal->ops->command(cmd->conn->client, \
35 cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args) \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name, \
40 silc_client_command_##func, \
41 silc_client_command_reply_##func, args)
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name) \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd, \
46 silc_client_command_##func, \
47 silc_client_command_reply_##func)
49 #define SAY cmd->conn->client->internal->ops->say
51 /************************ Static utility functions **************************/
53 /* Return next available command identifier. */
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
59 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
61 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
66 /* State to finish command thread after an error in resolving command */
68 SILC_FSM_STATE(silc_client_command_continue_error)
70 /* Destructor will free all resources */
71 return SILC_FSM_FINISH;
74 /* Command reply callback to continue with the execution of a command.
75 This will continue when first successful reply is received, and ignores
76 the rest. On the other hand, if only errors are received it will
77 wait for all errors before continuing. */
79 static SilcBool silc_client_command_continue(SilcClient client,
80 SilcClientConnection conn,
87 SilcClientCommandContext cmd = context;
89 /* Continue immediately when successful reply is received */
90 if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91 SILC_FSM_CALL_CONTINUE(&cmd->thread);
98 /* Continue after last error is received */
99 if (SILC_STATUS_IS_ERROR(status) ||
100 (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102 SILC_FSM_CALL_CONTINUE(&cmd->thread);
109 /* Continues after resolving completed. */
111 static void silc_client_command_resolve_continue(SilcClient client,
112 SilcClientConnection conn,
117 SilcClientCommandContext cmd = context;
119 if (status != SILC_STATUS_OK)
120 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
122 /* Continue with the command */
123 SILC_FSM_CALL_CONTINUE(&cmd->thread);
126 /* Dummy command callback. Nothing interesting to do here. Use this when
127 you just send command but don't care about reply. */
129 SilcBool silc_client_command_called_dummy(SilcClient client,
130 SilcClientConnection conn,
140 /* Dummy resolving callback. Nothing interesting to do here. Use this
141 when you just resolve entires but don't care about reply. */
143 void silc_client_command_resolve_dummy(SilcClient client,
144 SilcClientConnection conn,
152 /* Register command to client */
155 silc_client_command_register(SilcClient client,
158 SilcFSMStateCallback command_func,
159 SilcFSMStateCallback command_reply_func,
162 SilcClientCommand cmd;
164 cmd = silc_calloc(1, sizeof(*cmd));
168 cmd->command = command_func;
169 cmd->reply = command_reply_func;
170 cmd->max_args = max_args;
171 cmd->name = name ? strdup(name) : NULL;
177 silc_list_add(client->internal->commands, cmd);
182 /* Unregister command from client */
185 silc_client_command_unregister(SilcClient client,
187 SilcFSMStateCallback command_func,
188 SilcFSMStateCallback command_reply_func)
190 SilcClientCommand cmd;
192 silc_list_start(client->internal->commands);
193 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
194 if (cmd->cmd == command && cmd->command == command_func &&
195 cmd->reply == command_reply_func) {
196 silc_list_del(client->internal->commands, cmd);
197 silc_free(cmd->name);
206 /* Finds and returns a pointer to the command list. Return NULL if the
207 command is not found. */
209 static SilcClientCommand silc_client_command_find(SilcClient client,
212 SilcClientCommand cmd;
214 silc_list_start(client->internal->commands);
215 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
216 if (cmd->name && !strcasecmp(cmd->name, name))
223 /* Command thread destructor */
225 static void silc_client_command_destructor(SilcFSMThread thread,
227 void *destructor_context)
229 SilcClientCommandContext cmd = fsm_context;
230 SilcClientConnection conn = cmd->conn;
232 /* Removes commands that aren't waiting for reply but are waiting
233 for something. They may not have been removed yet. */
234 silc_list_del(conn->internal->pending_commands, cmd);
236 silc_client_command_free(cmd);
239 /* Add a command pending a command reply. Used internally by the library. */
242 silc_client_command_add_pending(SilcClientConnection conn,
243 SilcClientCommandContext cmd,
244 SilcClientCommandReply reply,
247 SilcClientCommandReplyCallback cb;
249 silc_mutex_lock(conn->internal->lock);
251 /* Add pending callback, if defined */
253 cb = silc_calloc(1, sizeof(*cb));
255 silc_mutex_unlock(conn->internal->lock);
259 cb->context = context;
260 silc_list_add(cmd->reply_callbacks, cb);
263 /* Add pending reply */
264 silc_list_add(conn->internal->pending_commands, cmd);
266 silc_mutex_unlock(conn->internal->lock);
271 /* Generic function to send any command. The arguments must be sent already
272 encoded into correct format and in correct order. Arguments come from
273 variable argument list pointer. */
275 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
276 SilcClientConnection conn,
277 SilcClientCommandContext cmd,
279 SilcClientCommandReply reply,
281 SilcUInt32 argc, va_list ap)
285 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
287 if (conn->internal->disconnected)
291 cmd->cmd_ident = silc_client_cmd_ident(conn);
293 /* Encode command payload */
294 packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
298 /* Send the command */
299 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
300 silc_buffer_datalen(packet))) {
301 silc_buffer_free(packet);
305 /* Add the command pending command reply */
306 silc_client_command_add_pending(conn, cmd, reply, reply_context);
308 silc_buffer_free(packet);
310 return cmd->cmd_ident;
313 /* Generic function to send any command. The arguments must be sent already
314 encoded into correct format and in correct order. Arguments come from
318 silc_client_command_send_arg_array(SilcClient client,
319 SilcClientConnection conn,
320 SilcClientCommandContext cmd,
322 SilcClientCommandReply reply,
325 unsigned char **argv,
326 SilcUInt32 *argv_lens,
327 SilcUInt32 *argv_types)
331 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
333 if (conn->internal->disconnected)
337 cmd->cmd_ident = silc_client_cmd_ident(conn);
339 /* Encode command payload */
340 packet = silc_command_payload_encode(command, argc, argv, argv_lens,
341 argv_types, cmd->cmd_ident);
345 /* Send the command */
346 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
347 silc_buffer_datalen(packet))) {
348 silc_buffer_free(packet);
352 /* Add the command pending command reply */
353 silc_client_command_add_pending(conn, cmd, reply, reply_context);
355 silc_buffer_free(packet);
357 return cmd->cmd_ident;
360 /* Generic function to send any command. The arguments must be sent already
361 encoded into correct format and in correct order. This is used internally
364 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
365 SilcClientCommandContext cmd,
367 SilcClientCommandReply reply,
369 SilcUInt32 argc, ...)
372 SilcUInt16 cmd_ident;
375 cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
376 reply, reply_context, argc, ap);
382 /****************************** Command API *********************************/
384 /* Free command context and its internals */
386 void silc_client_command_free(SilcClientCommandContext cmd)
388 SilcClientCommandReplyCallback cb;
391 for (i = 0; i < cmd->argc; i++)
392 silc_free(cmd->argv[i]);
393 silc_free(cmd->argv);
394 silc_free(cmd->argv_lens);
395 silc_free(cmd->argv_types);
397 silc_list_start(cmd->reply_callbacks);
398 while ((cb = silc_list_get(cmd->reply_callbacks)))
404 /* Executes a command */
406 SilcUInt16 silc_client_command_call(SilcClient client,
407 SilcClientConnection conn,
408 const char *command_line, ...)
412 unsigned char **argv = NULL;
413 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
414 SilcClientCommand command;
415 SilcClientCommandContext cmd;
419 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
420 "You are not connected to a server, please connect to server");
424 /* Parse arguments */
425 va_start(va, command_line);
429 /* Get command name */
430 command_name = silc_memdup(command_line, strcspn(command_line, " "));
434 /* Find command by name */
435 command = silc_client_command_find(client, command_name);
437 silc_free(command_name);
441 /* Parse command line */
442 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
443 &argv_types, &argc, command->max_args);
445 silc_free(command_name);
447 arg = va_arg(va, char *);
451 /* Find command by name */
452 command = silc_client_command_find(client, arg);
457 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
458 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
459 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
460 if (!argv || !argv_lens || !argv_types)
462 argv[argc] = silc_memdup(arg, strlen(arg));
465 argv_lens[argc] = strlen(arg);
466 argv_types[argc] = argc;
468 arg = va_arg(va, char *);
473 /* Allocate command context */
474 cmd = silc_calloc(1, sizeof(*cmd));
478 cmd->cmd = command->cmd;
481 cmd->argv_lens = argv_lens;
482 cmd->argv_types = argv_types;
483 cmd->cmd_ident = silc_client_cmd_ident(conn);
486 silc_list_init(cmd->reply_callbacks,
487 struct SilcClientCommandReplyCallbackStruct, next);
490 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
491 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
492 silc_client_command_destructor, NULL, FALSE);
493 silc_fsm_start_sync(&cmd->thread, command->command);
495 return cmd->cmd_ident;
498 /* Generic function to send any command. The arguments must be sent already
499 encoded into correct format and in correct order. */
501 SilcUInt16 silc_client_command_send(SilcClient client,
502 SilcClientConnection conn,
504 SilcClientCommandReply reply,
506 SilcUInt32 argc, ...)
508 SilcClientCommandContext cmd;
514 /* Allocate command context */
515 cmd = silc_calloc(1, sizeof(*cmd));
520 silc_list_init(cmd->reply_callbacks,
521 struct SilcClientCommandReplyCallbackStruct, next);
523 /* Send the command */
526 silc_client_command_send_vap(client, conn, cmd, command, reply,
527 reply_context, argc, ap);
530 if (!cmd->cmd_ident) {
531 silc_client_command_free(cmd);
535 /*** Wait for command reply */
536 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
537 silc_client_command_destructor, NULL, FALSE);
538 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
540 return cmd->cmd_ident;
543 /* Generic function to send any command. The arguments must be sent already
544 encoded into correct format and in correct order. Arguments come from
547 SilcUInt16 silc_client_command_send_argv(SilcClient client,
548 SilcClientConnection conn,
550 SilcClientCommandReply reply,
553 unsigned char **argv,
554 SilcUInt32 *argv_lens,
555 SilcUInt32 *argv_types)
557 SilcClientCommandContext cmd;
562 /* Allocate command context */
563 cmd = silc_calloc(1, sizeof(*cmd));
569 /* Send the command */
571 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
572 reply_context, argc, argv, argv_lens,
574 if (!cmd->cmd_ident) {
575 silc_client_command_free(cmd);
579 /*** Wait for command reply */
580 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
581 silc_client_command_destructor, NULL, FALSE);
582 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
584 return cmd->cmd_ident;
587 /* Attach to a command and command identifier to receive command reply. */
589 SilcBool silc_client_command_pending(SilcClientConnection conn,
592 SilcClientCommandReply reply,
595 SilcClientCommandContext cmd;
596 SilcClientCommandReplyCallback cb;
601 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
603 silc_mutex_lock(conn->internal->lock);
605 /* Find the pending command */
606 silc_list_start(conn->internal->pending_commands);
607 while ((cmd = silc_list_get(conn->internal->pending_commands)))
608 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
609 && cmd->cmd_ident == ident) {
611 /* Add the callback */
612 cb = silc_calloc(1, sizeof(*cb));
616 cb->context = context;
617 silc_list_add(cmd->reply_callbacks, cb);
620 silc_mutex_unlock(conn->internal->lock);
625 /******************************** WHOIS *************************************/
627 /* Command WHOIS. This command is used to query information about
630 SILC_FSM_STATE(silc_client_command_whois)
632 SilcClientCommandContext cmd = fsm_context;
633 SilcClientConnection conn = cmd->conn;
634 SilcClient client = conn->client;
635 SilcBuffer attrs = NULL;
636 unsigned char count[4], *tmp = NULL;
637 SilcBool details = FALSE, nick = FALSE;
638 unsigned char *pubkey = NULL;
639 char *nickname = NULL;
642 /* Given without arguments fetches client's own information */
644 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
645 silc_buffer_data(conn->internal->local_idp),
646 silc_buffer_len(conn->internal->local_idp));
648 /* Notify application */
649 COMMAND(SILC_STATUS_OK);
651 /** Wait for command reply */
652 silc_fsm_next(fsm, silc_client_command_reply_wait);
653 return SILC_FSM_CONTINUE;
656 for (i = 1; i < cmd->argc; i++) {
657 if (!strcasecmp(cmd->argv[i], "-details")) {
659 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
660 pubkey = cmd->argv[i + 1];
663 /* We assume that the first parameter is the nickname, if it isn't
664 -details or -pubkey. The last parameter should always be the count */
667 } else if (i == cmd->argc - 1) {
668 int c = atoi(cmd->argv[i]);
669 SILC_PUT32_MSB(c, count);
676 /* If pubkey is set, add all attributes to the attrs buffer, except
679 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
680 SILC_ATTRIBUTE_SERVICE,
681 SILC_ATTRIBUTE_STATUS_MOOD,
682 SILC_ATTRIBUTE_STATUS_FREETEXT,
683 SILC_ATTRIBUTE_STATUS_MESSAGE,
684 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
685 SILC_ATTRIBUTE_PREFERRED_CONTACT,
686 SILC_ATTRIBUTE_TIMEZONE,
687 SILC_ATTRIBUTE_GEOLOCATION,
688 SILC_ATTRIBUTE_DEVICE_INFO,
689 SILC_ATTRIBUTE_USER_ICON, 0);
691 attrs = silc_client_attributes_request(0);
696 SilcAttributeObjPk obj;
699 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
700 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
701 "Could not load public key %s, check the filename",
703 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
707 switch (silc_pkcs_get_type(pk)) {
709 obj.type = "silc-rsa";
712 obj.type = "ssh-rsa";
714 case SILC_PKCS_X509V3:
715 obj.type = "x509v3-sign-rsa";
717 case SILC_PKCS_OPENPGP:
718 obj.type = "pgp-sign-rsa";
724 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
726 attrs = silc_attribute_payload_encode(attrs,
727 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
728 SILC_ATTRIBUTE_FLAG_VALID,
732 silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname);
734 nickname = strdup(cmd->argv[1]);
737 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
738 3, 1, nick ? nickname : NULL,
739 nick ? strlen(nickname) : 0,
740 2, tmp ? tmp : NULL, tmp ? 4 : 0,
741 3, silc_buffer_datalen(attrs));
744 /* Notify application */
745 COMMAND(SILC_STATUS_OK);
747 /** Wait for command reply */
748 silc_fsm_next(fsm, silc_client_command_reply_wait);
749 return SILC_FSM_CONTINUE;
752 return SILC_FSM_FINISH;
755 /******************************** WHOWAS ************************************/
757 /* Command WHOWAS. This command is used to query history information about
758 specific user that used to exist in the network. */
760 SILC_FSM_STATE(silc_client_command_whowas)
762 SilcClientCommandContext cmd = fsm_context;
763 SilcClientConnection conn = cmd->conn;
764 unsigned char count[4];
767 if (cmd->argc < 2 || cmd->argc > 3) {
768 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
769 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
770 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
771 SILC_STATUS_ERR_TOO_MANY_PARAMS));
772 return SILC_FSM_FINISH;
775 if (cmd->argc == 2) {
776 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
777 1, 1, cmd->argv[1], cmd->argv_lens[1]);
779 c = atoi(cmd->argv[2]);
780 SILC_PUT32_MSB(c, count);
781 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
782 2, 1, cmd->argv[1], cmd->argv_lens[1],
783 2, count, sizeof(count));
786 /* Notify application */
787 COMMAND(SILC_STATUS_OK);
789 /** Wait for command reply */
790 silc_fsm_next(fsm, silc_client_command_reply_wait);
791 return SILC_FSM_CONTINUE;
794 /******************************** IDENTIFY **********************************/
796 /* Command IDENTIFY. This command is used to query information about
797 specific user, especially ID's. */
799 SILC_FSM_STATE(silc_client_command_identify)
801 SilcClientCommandContext cmd = fsm_context;
802 SilcClientConnection conn = cmd->conn;
803 unsigned char count[4];
806 if (cmd->argc < 2 || cmd->argc > 3)
807 return SILC_FSM_FINISH;
809 if (cmd->argc == 2) {
810 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
811 1, 1, cmd->argv[1], cmd->argv_lens[1]);
813 c = atoi(cmd->argv[2]);
814 SILC_PUT32_MSB(c, count);
815 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
816 2, 1, cmd->argv[1], cmd->argv_lens[1],
817 4, count, sizeof(count));
820 /** Wait for command reply */
821 silc_fsm_next(fsm, silc_client_command_reply_wait);
822 return SILC_FSM_CONTINUE;
825 /********************************** NICK ************************************/
827 /* Command NICK. Shows current nickname/sets new nickname on current
830 SILC_FSM_STATE(silc_client_command_nick)
832 SilcClientCommandContext cmd2, cmd = fsm_context;
833 SilcClientConnection conn = cmd->conn;
836 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
837 "Usage: /NICK <nickname>");
838 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
842 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
845 /* Show current nickname */
848 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
849 "Your nickname is %s on server %s",
850 conn->local_entry->nickname, conn->remote_host);
852 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
853 "Your nickname is %s", conn->local_entry->nickname);
856 COMMAND(SILC_STATUS_OK);
860 /* If JOIN command is active, wait for it to finish before sending NICK.
861 To avoid problems locally with changing IDs while joining, we do this. */
862 silc_mutex_lock(conn->internal->lock);
863 silc_list_start(conn->internal->pending_commands);
864 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
865 if (cmd2->cmd == SILC_COMMAND_JOIN) {
866 silc_mutex_unlock(conn->internal->lock);
867 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
868 return SILC_FSM_WAIT;
871 silc_mutex_unlock(conn->internal->lock);
873 if (cmd->argv_lens[1] > 128)
874 cmd->argv_lens[1] = 128;
876 /* Send the NICK command */
877 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
878 1, 1, cmd->argv[1], cmd->argv_lens[1]);
880 /* Notify application */
881 COMMAND(SILC_STATUS_OK);
883 /** Wait for command reply */
884 silc_fsm_next(fsm, silc_client_command_reply_wait);
885 return SILC_FSM_CONTINUE;
888 return SILC_FSM_FINISH;
891 /********************************** LIST ************************************/
893 /* Command LIST. Lists channels on the current server. */
895 SILC_FSM_STATE(silc_client_command_list)
897 SilcClientCommandContext cmd = fsm_context;
898 SilcClientConnection conn = cmd->conn;
899 SilcClient client = conn->client;
900 SilcChannelEntry channel = NULL;
901 SilcBuffer idp = NULL;
903 if (cmd->argc == 2) {
904 /* Get the Channel ID of the channel */
905 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
907 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
911 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
913 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
914 1, 1, silc_buffer_datalen(idp));
916 silc_buffer_free(idp);
917 silc_client_unref_channel(client, conn, channel);
919 /* Notify application */
920 COMMAND(SILC_STATUS_OK);
922 /** Wait for command reply */
923 silc_fsm_next(fsm, silc_client_command_reply_wait);
924 return SILC_FSM_CONTINUE;
927 /********************************** TOPIC ***********************************/
929 /* Command TOPIC. Sets/shows topic on a channel. */
931 SILC_FSM_STATE(silc_client_command_topic)
933 SilcClientCommandContext cmd = fsm_context;
934 SilcClientConnection conn = cmd->conn;
935 SilcClient client = conn->client;
936 SilcChannelEntry channel;
940 if (cmd->argc < 2 || cmd->argc > 3) {
941 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
942 "Usage: /TOPIC <channel> [<topic>]");
943 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
944 SILC_STATUS_ERR_TOO_MANY_PARAMS));
948 if (cmd->argv[1][0] == '*') {
949 if (!conn->current_channel) {
950 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
953 name = conn->current_channel->channel_name;
958 if (!conn->current_channel) {
959 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
963 /* Get the Channel ID of the channel */
964 channel = silc_client_get_channel(conn->client, conn, name);
966 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
970 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
972 /* Send TOPIC command to the server */
974 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
975 1, silc_buffer_datalen(idp),
976 2, cmd->argv[2], strlen(cmd->argv[2]));
978 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
979 1, silc_buffer_datalen(idp));
981 silc_buffer_free(idp);
982 silc_client_unref_channel(client, conn, channel);
984 /* Notify application */
985 COMMAND(SILC_STATUS_OK);
987 /** Wait for command reply */
988 silc_fsm_next(fsm, silc_client_command_reply_wait);
989 return SILC_FSM_CONTINUE;
992 return SILC_FSM_FINISH;
995 /********************************* INVITE ***********************************/
997 /* Command INVITE. Invites specific client to join a channel. This is
998 also used to mange the invite list of the channel. */
1000 SILC_FSM_STATE(silc_client_command_invite)
1002 SilcClientCommandContext cmd = fsm_context;
1003 SilcClientConnection conn = cmd->conn;
1004 SilcClient client = conn->client;
1005 SilcClientEntry client_entry = NULL;
1006 SilcChannelEntry channel = NULL;
1007 SilcBuffer clidp, chidp, args = NULL;
1008 SilcPublicKey pubkey = NULL;
1009 SilcDList clients = NULL;
1010 char *nickname = NULL, *name;
1011 char *invite = NULL;
1012 unsigned char action[1];
1014 if (cmd->argc < 2) {
1015 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1016 "Usage: /INVITE <channel> [<nickname>[@server>]"
1017 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1018 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1022 if (cmd->argv[1][0] == '*') {
1023 if (!conn->current_channel) {
1024 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1028 channel = conn->current_channel;
1029 silc_client_ref_channel(client, conn, channel);
1031 name = cmd->argv[1];
1033 channel = silc_client_get_channel(conn->client, conn, name);
1035 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1040 /* Parse the typed nickname. */
1041 if (cmd->argc == 3) {
1042 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1043 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1045 /* Find client entry */
1046 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1049 /* Resolve client information */
1050 SILC_FSM_CALL(silc_client_get_clients(
1051 client, conn, nickname, NULL,
1052 silc_client_command_resolve_continue,
1055 client_entry = silc_dlist_get(clients);
1057 if (cmd->argv[2][0] == '+')
1062 /* Check if it is public key file to be added to invite list */
1063 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1064 invite = cmd->argv[2];
1071 args = silc_buffer_alloc_size(2);
1072 silc_buffer_format(args,
1073 SILC_STR_UI_SHORT(1),
1076 chidp = silc_public_key_payload_encode(pubkey);
1077 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1078 silc_buffer_len(chidp), 2);
1079 silc_buffer_free(chidp);
1080 silc_pkcs_public_key_free(pubkey);
1082 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1086 /* Send the command */
1087 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1089 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1090 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1091 1, silc_buffer_datalen(chidp),
1092 2, silc_buffer_datalen(clidp),
1093 3, args ? action : NULL, args ? 1 : 0,
1094 4, silc_buffer_datalen(args));
1095 silc_buffer_free(clidp);
1097 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1098 1, silc_buffer_datalen(chidp),
1099 3, args ? action : NULL, args ? 1 : 0,
1100 4, silc_buffer_datalen(args));
1103 silc_buffer_free(chidp);
1104 silc_buffer_free(args);
1105 silc_free(nickname);
1106 silc_client_list_free(client, conn, clients);
1107 silc_client_unref_channel(client, conn, channel);
1109 /* Notify application */
1110 COMMAND(SILC_STATUS_OK);
1112 /** Wait for command reply */
1113 silc_fsm_next(fsm, silc_client_command_reply_wait);
1114 return SILC_FSM_CONTINUE;
1117 silc_free(nickname);
1118 return SILC_FSM_FINISH;
1121 /********************************** QUIT ************************************/
1123 /* Close the connection */
1125 SILC_FSM_STATE(silc_client_command_quit_final)
1127 SilcClientCommandContext cmd = fsm_context;
1128 SilcClientConnection conn = cmd->conn;
1130 SILC_LOG_DEBUG(("Quitting"));
1132 /* Notify application */
1133 COMMAND(SILC_STATUS_OK);
1135 /* Signal to close connection */
1136 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1137 if (!conn->internal->disconnected) {
1138 conn->internal->disconnected = TRUE;
1139 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1142 return SILC_FSM_FINISH;
1145 /* Command QUIT. Closes connection with current server. */
1147 SILC_FSM_STATE(silc_client_command_quit)
1149 SilcClientCommandContext cmd = fsm_context;
1150 SilcClientConnection conn = cmd->conn;
1153 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1154 1, cmd->argv[1], cmd->argv_lens[1]);
1156 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1158 /* Sleep for a while */
1161 /* We close the connection with a little timeout */
1162 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1163 return SILC_FSM_WAIT;
1166 /********************************** KILL ************************************/
1168 /* Command KILL. Router operator can use this command to remove an client
1169 fromthe SILC Network. */
1171 SILC_FSM_STATE(silc_client_command_kill)
1173 SilcClientCommandContext cmd = fsm_context;
1174 SilcClientConnection conn = cmd->conn;
1175 SilcClient client = conn->client;
1176 SilcBuffer idp, auth = NULL;
1177 SilcClientEntry target;
1179 char *nickname = NULL, *comment = NULL;
1181 if (cmd->argc < 2) {
1182 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1183 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1184 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1185 return SILC_FSM_FINISH;
1188 /* Parse the typed nickname. */
1189 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1190 return SILC_FSM_FINISH;
1192 /* Get the target client */
1193 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1195 /* Resolve client information */
1196 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1197 silc_client_command_resolve_continue,
1200 target = silc_dlist_get(clients);
1202 if (cmd->argc >= 3) {
1203 if (strcasecmp(cmd->argv[2], "-pubkey"))
1204 comment = cmd->argv[2];
1206 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1207 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1208 /* Encode the public key authentication payload */
1209 auth = silc_auth_public_key_auth_generate(conn->public_key,
1212 conn->internal->sha1hash,
1213 &target->id, SILC_ID_CLIENT);
1217 /* Send the KILL command to the server */
1218 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1219 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1220 1, silc_buffer_datalen(idp),
1221 2, comment, comment ? strlen(comment) : 0,
1222 3, silc_buffer_datalen(auth));
1223 silc_buffer_free(idp);
1224 silc_buffer_free(auth);
1225 silc_free(nickname);
1226 silc_client_list_free(client, conn, clients);
1228 /* Notify application */
1229 COMMAND(SILC_STATUS_OK);
1231 /** Wait for command reply */
1232 silc_fsm_next(fsm, silc_client_command_reply_wait);
1233 return SILC_FSM_CONTINUE;
1236 /********************************** INFO ************************************/
1238 /* Command INFO. Request information about specific server. If specific
1239 server is not provided the current server is used. */
1241 SILC_FSM_STATE(silc_client_command_info)
1243 SilcClientCommandContext cmd = fsm_context;
1244 SilcClientConnection conn = cmd->conn;
1246 /* Send the command */
1248 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1249 1, cmd->argv[1], cmd->argv_lens[1]);
1251 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1253 /* Notify application */
1254 COMMAND(SILC_STATUS_OK);
1256 /** Wait for command reply */
1257 silc_fsm_next(fsm, silc_client_command_reply_wait);
1258 return SILC_FSM_CONTINUE;
1261 /********************************** STATS ***********************************/
1263 /* Command STATS. Shows server and network statistics. */
1265 SILC_FSM_STATE(silc_client_command_stats)
1267 SilcClientCommandContext cmd = fsm_context;
1268 SilcClientConnection conn = cmd->conn;
1270 /* Send the command */
1271 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1272 1, silc_buffer_datalen(conn->internal->
1275 /* Notify application */
1276 COMMAND(SILC_STATUS_OK);
1278 /** Wait for command reply */
1279 silc_fsm_next(fsm, silc_client_command_reply_wait);
1280 return SILC_FSM_CONTINUE;
1283 /********************************** PING ************************************/
1285 /* Command PING. Sends ping to server. */
1287 SILC_FSM_STATE(silc_client_command_ping)
1289 SilcClientCommandContext cmd = fsm_context;
1290 SilcClientConnection conn = cmd->conn;
1292 if (cmd->argc < 2) {
1293 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1294 return SILC_FSM_FINISH;
1297 /* Send the command */
1298 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1299 1, silc_buffer_datalen(conn->internal->
1302 /* Save ping time */
1303 cmd->context = SILC_64_TO_PTR(silc_time());
1305 /* Notify application */
1306 COMMAND(SILC_STATUS_OK);
1308 /** Wait for command reply */
1309 silc_fsm_next(fsm, silc_client_command_reply_wait);
1310 return SILC_FSM_CONTINUE;
1313 /********************************** JOIN ************************************/
1315 /* Command JOIN. Joins to a channel. */
1317 SILC_FSM_STATE(silc_client_command_join)
1319 SilcClientCommandContext cmd2, cmd = fsm_context;
1320 SilcClientConnection conn = cmd->conn;
1321 SilcClient client = conn->client;
1322 SilcChannelEntry channel = NULL;
1323 SilcBuffer auth = NULL, cauth = NULL;
1324 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1325 int i, passphrase_len = 0;
1327 if (cmd->argc < 2) {
1328 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1332 /* See if we have joined to the requested channel already */
1333 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1334 if (channel && silc_client_on_channel(channel, conn->local_entry))
1337 /* If NICK command is active, wait for it to finish before sending JOIN.
1338 To avoid problems locally with changing IDs while joining, we do this. */
1339 silc_mutex_lock(conn->internal->lock);
1340 silc_list_start(conn->internal->pending_commands);
1341 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1342 if (cmd2->cmd == SILC_COMMAND_NICK) {
1343 silc_mutex_unlock(conn->internal->lock);
1344 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1345 return SILC_FSM_WAIT;
1348 silc_mutex_unlock(conn->internal->lock);
1350 if (cmd->argv_lens[1] > 256)
1351 cmd->argv_lens[1] = 256;
1353 name = cmd->argv[1];
1355 for (i = 2; i < cmd->argc; i++) {
1356 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1357 cipher = cmd->argv[++i];
1358 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1359 hmac = cmd->argv[++i];
1360 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1361 auth = silc_auth_public_key_auth_generate(conn->public_key,
1364 conn->internal->sha1hash,
1367 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1368 SilcPublicKey pubkey = conn->public_key;
1369 SilcPrivateKey privkey = conn->private_key;
1370 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1373 if (cmd->argc >= i + 3) {
1375 if (cmd->argc >= i + 4) {
1376 pass = cmd->argv[i + 3];
1379 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1380 &pubkey, &privkey)) {
1381 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1382 "Could not load key pair, check your arguments");
1383 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1389 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1390 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1392 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1393 memcpy(pubdata, pkhash, 20);
1394 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1396 conn->internal->sha1hash,
1399 memset(pubdata, 0, 128);
1402 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1403 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1404 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1405 cmd->argv_lens[i], 0);
1406 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1407 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1408 0, pu8, passphrase_len);
1411 passphrase = strdup(cmd->argv[i]);
1412 passphrase_len = cmd->argv_lens[i];
1417 /* Send JOIN command to the server */
1418 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1419 1, name, strlen(name),
1420 2, silc_buffer_datalen(conn->internal->
1422 3, passphrase, passphrase_len,
1423 4, cipher, cipher ? strlen(cipher) : 0,
1424 5, hmac, hmac ? strlen(hmac) : 0,
1425 6, silc_buffer_datalen(auth),
1426 7, silc_buffer_datalen(cauth));
1428 silc_buffer_free(auth);
1429 silc_buffer_free(cauth);
1431 memset(passphrase, 0, strlen(passphrase));
1432 silc_free(passphrase);
1433 silc_client_unref_channel(client, conn, channel);
1435 /* Notify application */
1436 COMMAND(SILC_STATUS_OK);
1438 /** Wait for command reply */
1439 silc_fsm_next(fsm, silc_client_command_reply_wait);
1440 return SILC_FSM_CONTINUE;
1443 silc_client_unref_channel(client, conn, channel);
1444 return SILC_FSM_FINISH;
1447 /********************************** MOTD ************************************/
1449 /* MOTD command. Requests motd from server. */
1451 SILC_FSM_STATE(silc_client_command_motd)
1453 SilcClientCommandContext cmd = fsm_context;
1454 SilcClientConnection conn = cmd->conn;
1456 if (cmd->argc < 1 || cmd->argc > 2) {
1457 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1458 "Usage: /MOTD [<server>]");
1459 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1460 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1461 return SILC_FSM_FINISH;
1464 /* Send the command */
1466 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1467 1, conn->remote_host,
1468 strlen(conn->remote_host));
1470 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1471 1, cmd->argv[1], cmd->argv_lens[1]);
1473 /* Notify application */
1474 COMMAND(SILC_STATUS_OK);
1476 /** Wait for command reply */
1477 silc_fsm_next(fsm, silc_client_command_reply_wait);
1478 return SILC_FSM_CONTINUE;
1481 /********************************** UMODE ***********************************/
1483 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1484 modes as client cannot set itself server/router operator privileges. */
1486 SILC_FSM_STATE(silc_client_command_umode)
1488 SilcClientCommandContext cmd = fsm_context;
1489 SilcClientConnection conn = cmd->conn;
1490 unsigned char *cp, modebuf[4];
1491 SilcUInt32 mode, add, len;
1494 if (cmd->argc < 2) {
1495 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1496 "Usage: /UMODE +|-<modes>");
1497 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1498 return SILC_FSM_FINISH;
1501 mode = conn->local_entry->mode;
1503 /* Are we adding or removing mode */
1504 if (cmd->argv[1][0] == '-')
1510 cp = cmd->argv[1] + 1;
1512 for (i = 0; i < len; i++) {
1517 mode |= SILC_UMODE_SERVER_OPERATOR;
1518 mode |= SILC_UMODE_ROUTER_OPERATOR;
1519 mode |= SILC_UMODE_GONE;
1520 mode |= SILC_UMODE_INDISPOSED;
1521 mode |= SILC_UMODE_BUSY;
1522 mode |= SILC_UMODE_PAGE;
1523 mode |= SILC_UMODE_HYPER;
1524 mode |= SILC_UMODE_ROBOT;
1525 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1526 mode |= SILC_UMODE_REJECT_WATCHING;
1528 mode = SILC_UMODE_NONE;
1533 mode |= SILC_UMODE_SERVER_OPERATOR;
1535 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1539 mode |= SILC_UMODE_ROUTER_OPERATOR;
1541 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1545 mode |= SILC_UMODE_GONE;
1547 mode &= ~SILC_UMODE_GONE;
1551 mode |= SILC_UMODE_INDISPOSED;
1553 mode &= ~SILC_UMODE_INDISPOSED;
1557 mode |= SILC_UMODE_BUSY;
1559 mode &= ~SILC_UMODE_BUSY;
1563 mode |= SILC_UMODE_PAGE;
1565 mode &= ~SILC_UMODE_PAGE;
1569 mode |= SILC_UMODE_HYPER;
1571 mode &= ~SILC_UMODE_HYPER;
1575 mode |= SILC_UMODE_ROBOT;
1577 mode &= ~SILC_UMODE_ROBOT;
1581 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1583 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1587 mode |= SILC_UMODE_REJECT_WATCHING;
1589 mode &= ~SILC_UMODE_REJECT_WATCHING;
1593 mode |= SILC_UMODE_BLOCK_INVITE;
1595 mode &= ~SILC_UMODE_BLOCK_INVITE;
1598 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1599 return SILC_FSM_FINISH;
1604 SILC_PUT32_MSB(mode, modebuf);
1606 /* Send the command */
1607 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1608 1, silc_buffer_datalen(conn->internal->
1610 2, modebuf, sizeof(modebuf));
1612 /* Notify application */
1613 COMMAND(SILC_STATUS_OK);
1615 /** Wait for command reply */
1616 silc_fsm_next(fsm, silc_client_command_reply_wait);
1617 return SILC_FSM_CONTINUE;
1620 /********************************** CMODE ***********************************/
1622 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1623 can be set several at once. Those modes that require argument must be set
1624 separately (unless set with modes that does not require arguments). */
1626 SILC_FSM_STATE(silc_client_command_cmode)
1628 SilcClientCommandContext cmd = fsm_context;
1629 SilcClientConnection conn = cmd->conn;
1630 SilcClient client = conn->client;
1631 SilcChannelEntry channel = NULL;
1632 SilcBuffer chidp, auth = NULL, pk = NULL;
1633 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1634 SilcUInt32 mode, add, type, len, arg_len = 0;
1637 if (cmd->argc < 3) {
1638 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1639 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1640 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1644 if (cmd->argv[1][0] == '*') {
1645 if (!conn->current_channel) {
1646 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1650 channel = conn->current_channel;
1651 silc_client_ref_channel(client, conn, channel);
1653 name = cmd->argv[1];
1655 channel = silc_client_get_channel(conn->client, conn, name);
1657 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1662 mode = channel->mode;
1664 /* Are we adding or removing mode */
1665 if (cmd->argv[2][0] == '-')
1670 /* Argument type to be sent to server */
1674 cp = cmd->argv[2] + 1;
1676 for (i = 0; i < len; i++) {
1680 mode |= SILC_CHANNEL_MODE_PRIVATE;
1682 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1686 mode |= SILC_CHANNEL_MODE_SECRET;
1688 mode &= ~SILC_CHANNEL_MODE_SECRET;
1692 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1694 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1698 mode |= SILC_CHANNEL_MODE_INVITE;
1700 mode &= ~SILC_CHANNEL_MODE_INVITE;
1704 mode |= SILC_CHANNEL_MODE_TOPIC;
1706 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1710 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1712 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1716 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1718 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1723 mode |= SILC_CHANNEL_MODE_ULIMIT;
1725 if (cmd->argc < 4) {
1726 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1727 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1728 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1731 ll = atoi(cmd->argv[3]);
1732 SILC_PUT32_MSB(ll, tmp);
1736 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1741 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1743 if (cmd->argc < 4) {
1744 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1745 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1746 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1750 arg_len = cmd->argv_lens[3];
1752 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1757 mode |= SILC_CHANNEL_MODE_CIPHER;
1759 if (cmd->argc < 4) {
1760 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1761 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1762 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1766 arg_len = cmd->argv_lens[3];
1768 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1773 mode |= SILC_CHANNEL_MODE_HMAC;
1775 if (cmd->argc < 4) {
1776 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1777 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1778 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1782 arg_len = cmd->argv_lens[3];
1784 mode &= ~SILC_CHANNEL_MODE_HMAC;
1789 SilcPublicKey pubkey = conn->public_key;
1790 SilcPrivateKey privkey = conn->private_key;
1792 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1795 if (cmd->argc >= 5) {
1798 pass = cmd->argv[5];
1799 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1800 &pubkey, &privkey)) {
1801 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1802 "Could not load key pair, check your arguments");
1803 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1808 pk = silc_public_key_payload_encode(pubkey);
1809 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1811 conn->internal->sha1hash,
1814 arg = silc_buffer_data(auth);
1815 arg_len = silc_buffer_len(auth);
1817 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1823 SilcBool chadd = FALSE;
1824 SilcPublicKey chpk = NULL;
1826 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1829 if (cmd->argc == 3) {
1830 /* Send empty command to receive the public key list. */
1831 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1832 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1834 1, silc_buffer_datalen(chidp));
1835 silc_buffer_free(chidp);
1837 /* Notify application */
1838 COMMAND(SILC_STATUS_OK);
1842 if (cmd->argc >= 4) {
1843 auth = silc_buffer_alloc_size(2);
1844 silc_buffer_format(auth,
1845 SILC_STR_UI_SHORT(cmd->argc - 3),
1849 for (k = 3; k < cmd->argc; k++) {
1850 if (cmd->argv[k][0] == '+')
1852 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1853 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1854 "Could not load public key %s, check the filename",
1856 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1857 silc_buffer_free(auth);
1862 pk = silc_public_key_payload_encode(chpk);
1863 auth = silc_argument_payload_encode_one(auth,
1864 silc_buffer_datalen(pk),
1865 chadd ? 0x00 : 0x01);
1866 silc_pkcs_public_key_free(chpk);
1867 silc_buffer_free(pk);
1872 arg = silc_buffer_data(auth);
1873 arg_len = silc_buffer_len(auth);
1875 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1879 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1885 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1886 SILC_PUT32_MSB(mode, modebuf);
1888 /* Send the command. We support sending only one mode at once that
1889 requires an argument. */
1891 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1892 1, silc_buffer_datalen(chidp),
1893 2, modebuf, sizeof(modebuf),
1895 8, silc_buffer_datalen(pk));
1897 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1898 1, silc_buffer_datalen(chidp),
1899 2, modebuf, sizeof(modebuf));
1902 silc_buffer_free(chidp);
1903 silc_buffer_free(auth);
1904 silc_buffer_free(pk);
1905 silc_client_unref_channel(client, conn, channel);
1907 /* Notify application */
1908 COMMAND(SILC_STATUS_OK);
1910 /** Wait for command reply */
1911 silc_fsm_next(fsm, silc_client_command_reply_wait);
1912 return SILC_FSM_CONTINUE;
1915 silc_client_unref_channel(client, conn, channel);
1916 return SILC_FSM_FINISH;
1919 /********************************* CUMODE ***********************************/
1921 /* CUMODE command. Changes client's mode on a channel. */
1923 SILC_FSM_STATE(silc_client_command_cumode)
1925 SilcClientCommandContext cmd = fsm_context;
1926 SilcClientConnection conn = cmd->conn;
1927 SilcClient client = conn->client;
1928 SilcChannelEntry channel = NULL;
1929 SilcChannelUser chu;
1930 SilcClientEntry client_entry;
1931 SilcBuffer clidp, chidp, auth = NULL;
1932 SilcDList clients = NULL;
1933 unsigned char *name, *cp, modebuf[4];
1934 SilcUInt32 mode = 0, add, len;
1935 char *nickname = NULL;
1938 if (cmd->argc < 4) {
1939 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1940 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1941 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1945 if (cmd->argv[1][0] == '*') {
1946 if (!conn->current_channel) {
1947 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1951 channel = conn->current_channel;
1952 silc_client_ref_channel(client, conn, channel);
1954 name = cmd->argv[1];
1956 channel = silc_client_get_channel(conn->client, conn, name);
1958 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1963 /* Parse the typed nickname. */
1964 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1966 /* Find client entry */
1967 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
1969 /* Resolve client information */
1970 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1971 silc_client_command_resolve_continue,
1974 client_entry = silc_dlist_get(clients);
1976 /* Get the current mode */
1977 chu = silc_client_on_channel(channel, client_entry);
1981 /* Are we adding or removing mode */
1982 if (cmd->argv[2][0] == '-')
1988 cp = cmd->argv[2] + 1;
1990 for (i = 0; i < len; i++) {
1994 mode |= SILC_CHANNEL_UMODE_CHANFO;
1995 mode |= SILC_CHANNEL_UMODE_CHANOP;
1996 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1997 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1998 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2000 mode = SILC_CHANNEL_UMODE_NONE;
2005 SilcPublicKey pubkey = conn->public_key;
2006 SilcPrivateKey privkey = conn->private_key;
2008 if (cmd->argc >= 6) {
2011 pass = cmd->argv[6];
2012 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2013 &pubkey, &privkey)) {
2014 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2015 "Could not load key pair, check your arguments");
2016 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2021 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
2023 conn->internal->sha1hash,
2026 mode |= SILC_CHANNEL_UMODE_CHANFO;
2028 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2033 mode |= SILC_CHANNEL_UMODE_CHANOP;
2035 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2039 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2041 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2045 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2047 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2051 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2053 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2057 mode |= SILC_CHANNEL_UMODE_QUIET;
2059 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2062 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2068 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2069 SILC_PUT32_MSB(mode, modebuf);
2070 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2072 /* Send the command packet. We support sending only one mode at once
2073 that requires an argument. */
2074 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2075 1, silc_buffer_datalen(chidp),
2077 3, silc_buffer_datalen(clidp),
2078 4, silc_buffer_datalen(auth));
2080 silc_buffer_free(chidp);
2081 silc_buffer_free(clidp);
2083 silc_buffer_free(auth);
2084 silc_free(nickname);
2085 silc_client_list_free(client, conn, clients);
2086 silc_client_unref_channel(client, conn, channel);
2088 /* Notify application */
2089 COMMAND(SILC_STATUS_OK);
2091 /** Wait for command reply */
2092 silc_fsm_next(fsm, silc_client_command_reply_wait);
2093 return SILC_FSM_CONTINUE;
2096 silc_client_unref_channel(client, conn, channel);
2097 silc_client_list_free(client, conn, clients);
2098 silc_free(nickname);
2099 return SILC_FSM_FINISH;
2102 /********************************** KICK ************************************/
2104 /* KICK command. Kicks a client out of channel. */
2106 SILC_FSM_STATE(silc_client_command_kick)
2108 SilcClientCommandContext cmd = fsm_context;
2109 SilcClientConnection conn = cmd->conn;
2110 SilcClient client = conn->client;
2111 SilcChannelEntry channel = NULL;
2112 SilcBuffer idp, idp2;
2113 SilcClientEntry target;
2114 SilcDList clients = NULL;
2117 if (cmd->argc < 3) {
2118 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2119 "Usage: /KICK <channel> <nickname> [<comment>]");
2120 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2124 if (cmd->argv[1][0] == '*') {
2125 if (!conn->current_channel) {
2126 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2129 name = conn->current_channel->channel_name;
2131 name = cmd->argv[1];
2134 if (!conn->current_channel) {
2135 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2139 /* Get the Channel ID of the channel */
2140 channel = silc_client_get_channel(conn->client, conn, name);
2142 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2146 /* Get the target client */
2147 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2149 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2150 "No such client: %s", cmd->argv[2]);
2151 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2154 target = silc_dlist_get(clients);
2156 /* Send KICK command to the server */
2157 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2158 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2160 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2161 1, silc_buffer_datalen(idp),
2162 2, silc_buffer_datalen(idp2));
2164 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2165 1, silc_buffer_datalen(idp),
2166 2, silc_buffer_datalen(idp2),
2167 3, cmd->argv[3], strlen(cmd->argv[3]));
2169 silc_buffer_free(idp);
2170 silc_buffer_free(idp2);
2171 silc_client_list_free(client, conn, clients);
2172 silc_client_unref_channel(client, conn, channel);
2174 /* Notify application */
2175 COMMAND(SILC_STATUS_OK);
2177 /** Wait for command reply */
2178 silc_fsm_next(fsm, silc_client_command_reply_wait);
2179 return SILC_FSM_CONTINUE;
2182 silc_client_unref_channel(client, conn, channel);
2183 return SILC_FSM_FINISH;
2186 /***************************** OPER & SILCOPER ******************************/
2189 unsigned char *passphrase;
2190 SilcUInt32 passphrase_len;
2191 } *SilcClientCommandOper;
2193 /* Ask passphrase callback */
2195 static void silc_client_command_oper_cb(unsigned char *data,
2196 SilcUInt32 data_len, void *context)
2198 SilcClientCommandContext cmd = context;
2199 SilcClientCommandOper oper = cmd->context;
2201 if (data && data_len)
2202 oper->passphrase = silc_memdup(data, data_len);
2203 oper->passphrase_len = data_len;
2206 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2209 /* Send OPER/SILCOPER command */
2211 SILC_FSM_STATE(silc_client_command_oper_send)
2213 SilcClientCommandContext cmd = fsm_context;
2214 SilcClientConnection conn = cmd->conn;
2215 SilcClientCommandOper oper = cmd->context;
2218 if (!oper || !oper->passphrase) {
2219 /* Encode the public key authentication payload */
2220 auth = silc_auth_public_key_auth_generate(conn->public_key,
2223 conn->internal->hash,
2227 /* Encode the password authentication payload */
2228 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2229 oper->passphrase, oper->passphrase_len);
2232 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2233 1, cmd->argv[1], strlen(cmd->argv[1]),
2234 2, silc_buffer_datalen(auth));
2236 silc_buffer_clear(auth);
2237 silc_buffer_free(auth);
2239 silc_free(oper->passphrase);
2243 /* Notify application */
2244 COMMAND(SILC_STATUS_OK);
2246 /** Wait for command reply */
2247 silc_fsm_next(fsm, silc_client_command_reply_wait);
2248 return SILC_FSM_CONTINUE;
2251 /* OPER command. Used to obtain server operator privileges. */
2253 SILC_FSM_STATE(silc_client_command_oper)
2255 SilcClientCommandContext cmd = fsm_context;
2256 SilcClientConnection conn = cmd->conn;
2257 SilcClientCommandOper oper;
2259 if (cmd->argc < 2) {
2260 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2261 "Usage: /OPER <username> [-pubkey]");
2262 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2263 return SILC_FSM_FINISH;
2266 /* Get passphrase */
2267 if (cmd->argc < 3) {
2268 oper = silc_calloc(1, sizeof(*oper));
2270 return SILC_FSM_FINISH;
2271 cmd->context = oper;
2272 SILC_FSM_CALL(conn->client->internal->
2273 ops->ask_passphrase(conn->client, conn,
2274 silc_client_command_oper_cb, cmd));
2277 silc_fsm_next(fsm, silc_client_command_oper_send);
2278 return SILC_FSM_CONTINUE;
2281 /* SILCOPER command. Used to obtain router operator privileges. */
2283 SILC_FSM_STATE(silc_client_command_silcoper)
2285 SilcClientCommandContext cmd = fsm_context;
2286 SilcClientConnection conn = cmd->conn;
2287 SilcClientCommandOper oper;
2289 if (cmd->argc < 2) {
2290 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2291 "Usage: /SILCOPER <username> [-pubkey]");
2292 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2293 return SILC_FSM_FINISH;
2296 /* Get passphrase */
2297 if (cmd->argc < 3) {
2298 oper = silc_calloc(1, sizeof(*oper));
2300 return SILC_FSM_FINISH;
2301 cmd->context = oper;
2302 SILC_FSM_CALL(conn->client->internal->
2303 ops->ask_passphrase(conn->client, conn,
2304 silc_client_command_oper_cb, cmd));
2307 silc_fsm_next(fsm, silc_client_command_oper_send);
2308 return SILC_FSM_CONTINUE;
2311 /*********************************** BAN ************************************/
2313 /* Command BAN. This is used to manage the ban list of the channel. */
2315 SILC_FSM_STATE(silc_client_command_ban)
2317 SilcClientCommandContext cmd = fsm_context;
2318 SilcClientConnection conn = cmd->conn;
2319 SilcClient client = conn->client;
2320 SilcChannelEntry channel;
2321 SilcBuffer chidp, args = NULL;
2322 char *name, *ban = NULL;
2323 unsigned char action[1];
2324 SilcPublicKey pubkey = NULL;
2326 if (cmd->argc < 2) {
2327 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2328 "Usage: /BAN <channel> "
2329 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2330 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2334 if (cmd->argv[1][0] == '*') {
2335 if (!conn->current_channel) {
2336 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2340 channel = conn->current_channel;
2341 silc_client_ref_channel(client, conn, channel);
2343 name = cmd->argv[1];
2345 channel = silc_client_get_channel(conn->client, conn, name);
2347 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2352 if (cmd->argc == 3) {
2353 if (cmd->argv[2][0] == '+')
2358 /* Check if it is public key file to be added to invite list */
2359 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2366 args = silc_buffer_alloc_size(2);
2367 silc_buffer_format(args,
2368 SILC_STR_UI_SHORT(1),
2371 chidp = silc_public_key_payload_encode(pubkey);
2372 args = silc_argument_payload_encode_one(args,
2373 silc_buffer_datalen(chidp), 2);
2374 silc_buffer_free(chidp);
2375 silc_pkcs_public_key_free(pubkey);
2377 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2381 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2383 /* Send the command */
2384 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2385 1, silc_buffer_datalen(chidp),
2386 2, args ? action : NULL, args ? 1 : 0,
2387 3, silc_buffer_datalen(args));
2389 silc_buffer_free(chidp);
2390 silc_buffer_free(args);
2391 silc_client_unref_channel(client, conn, channel);
2393 /* Notify application */
2394 COMMAND(SILC_STATUS_OK);
2396 /** Wait for command reply */
2397 silc_fsm_next(fsm, silc_client_command_reply_wait);
2398 return SILC_FSM_CONTINUE;
2401 return SILC_FSM_FINISH;
2404 /********************************* DETACH ***********************************/
2406 /* Command DETACH. This is used to detach from the server */
2408 SILC_FSM_STATE(silc_client_command_detach)
2410 SilcClientCommandContext cmd = fsm_context;
2411 SilcClientConnection conn = cmd->conn;
2413 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2415 /* Notify application */
2416 COMMAND(SILC_STATUS_OK);
2418 /** Wait for command reply */
2419 silc_fsm_next(fsm, silc_client_command_reply_wait);
2420 return SILC_FSM_CONTINUE;
2423 /********************************** WATCH ***********************************/
2425 /* Command WATCH. */
2427 SILC_FSM_STATE(silc_client_command_watch)
2429 SilcClientCommandContext cmd = fsm_context;
2430 SilcClientConnection conn = cmd->conn;
2431 SilcBuffer args = NULL;
2433 const char *pubkey = NULL;
2434 SilcBool pubkey_add = TRUE;
2436 if (cmd->argc < 3) {
2437 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2441 if (!strcasecmp(cmd->argv[1], "-add")) {
2443 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2445 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2447 pubkey = cmd->argv[2] + 1;
2448 if (cmd->argv[2][0] == '-')
2451 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2459 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2460 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2461 "Could not load public key %s, check the filename", pubkey);
2462 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2466 args = silc_buffer_alloc_size(2);
2467 silc_buffer_format(args,
2468 SILC_STR_UI_SHORT(1),
2470 buffer = silc_public_key_payload_encode(pk);
2471 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2472 pubkey_add ? 0x00 : 0x01);
2473 silc_buffer_free(buffer);
2474 silc_pkcs_public_key_free(pk);
2477 /* If watching by nickname, resolve all users with that nickname so that
2478 we get their information immediately. */
2480 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2481 silc_client_command_resolve_dummy, NULL);
2483 /* Send the commmand */
2484 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2485 1, silc_buffer_datalen(conn->internal->
2487 type, pubkey ? args->data : cmd->argv[2],
2488 pubkey ? silc_buffer_len(args) :
2491 silc_buffer_free(args);
2493 /* Notify application */
2494 COMMAND(SILC_STATUS_OK);
2496 /** Wait for command reply */
2497 silc_fsm_next(fsm, silc_client_command_reply_wait);
2498 return SILC_FSM_CONTINUE;
2501 return SILC_FSM_FINISH;
2504 /********************************** LEAVE ***********************************/
2506 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2508 SILC_FSM_STATE(silc_client_command_leave)
2510 SilcClientCommandContext cmd = fsm_context;
2511 SilcClientConnection conn = cmd->conn;
2512 SilcClient client = conn->client;
2513 SilcChannelEntry channel;
2517 if (cmd->argc != 2) {
2518 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2519 "Usage: /LEAVE <channel>");
2520 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2524 if (cmd->argv[1][0] == '*') {
2525 if (!conn->current_channel) {
2526 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2529 name = conn->current_channel->channel_name;
2531 name = cmd->argv[1];
2534 /* Get the channel entry */
2535 channel = silc_client_get_channel(conn->client, conn, name);
2537 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2541 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2543 /* Send LEAVE command to the server */
2544 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2545 1, silc_buffer_datalen(idp));
2547 silc_buffer_free(idp);
2549 /* Notify application */
2550 COMMAND(SILC_STATUS_OK);
2552 if (conn->current_channel == channel)
2553 conn->current_channel = NULL;
2555 silc_client_unref_channel(client, conn, channel);
2557 /** Wait for command reply */
2558 silc_fsm_next(fsm, silc_client_command_reply_wait);
2559 return SILC_FSM_CONTINUE;
2562 return SILC_FSM_FINISH;
2565 /********************************** USERS ***********************************/
2567 /* Command USERS. Requests the USERS of the clients joined on requested
2570 SILC_FSM_STATE(silc_client_command_users)
2572 SilcClientCommandContext cmd = fsm_context;
2573 SilcClientConnection conn = cmd->conn;
2576 if (cmd->argc != 2) {
2577 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2578 "Usage: /USERS <channel>");
2579 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2583 if (cmd->argv[1][0] == '*') {
2584 if (!conn->current_channel) {
2585 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2588 name = conn->current_channel->channel_name;
2590 name = cmd->argv[1];
2593 /* Send USERS command to the server */
2594 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2595 2, name, strlen(name));
2597 /* Notify application */
2598 COMMAND(SILC_STATUS_OK);
2600 /** Wait for command reply */
2601 silc_fsm_next(fsm, silc_client_command_reply_wait);
2602 return SILC_FSM_CONTINUE;
2605 return SILC_FSM_FINISH;
2608 /********************************* GETKEY ***********************************/
2610 /* Command GETKEY. Used to fetch remote client's public key. */
2612 SILC_FSM_STATE(silc_client_command_getkey)
2614 SilcClientCommandContext cmd = fsm_context;
2615 SilcClientConnection conn = cmd->conn;
2616 SilcClient client = conn->client;
2617 SilcClientEntry client_entry;
2618 SilcServerEntry server_entry;
2622 if (cmd->argc < 2) {
2623 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2624 "Usage: /GETKEY <nickname or server name>");
2625 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2626 return SILC_FSM_FINISH;
2629 /* Find client entry */
2630 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2632 /* Check whether user requested server */
2633 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2634 if (!server_entry) {
2635 if (cmd->resolved) {
2636 /* Resolving didn't find anything. We should never get here as
2637 errors are handled in the resolving callback. */
2638 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2639 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2640 return SILC_FSM_FINISH;
2643 /* No client or server exist with this name, query for both. */
2644 cmd->resolved = TRUE;
2645 SILC_FSM_CALL(silc_client_command_send(client, conn,
2646 SILC_COMMAND_IDENTIFY,
2647 silc_client_command_continue,
2650 strlen(cmd->argv[1]),
2652 strlen(cmd->argv[1])));
2655 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2656 silc_client_unref_server(client, conn, server_entry);
2658 client_entry = silc_dlist_get(clients);
2659 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2660 silc_client_list_free(client, conn, clients);
2663 /* Send the commmand */
2664 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2665 1, silc_buffer_datalen(idp));
2667 silc_buffer_free(idp);
2669 /* Notify application */
2670 COMMAND(SILC_STATUS_OK);
2672 /** Wait for command reply */
2673 silc_fsm_next(fsm, silc_client_command_reply_wait);
2674 return SILC_FSM_CONTINUE;
2677 /********************************* SERVICE **********************************/
2679 /* Command SERVICE. Negotiates service agreement with server. */
2680 /* XXX incomplete */
2682 SILC_FSM_STATE(silc_client_command_service)
2684 SilcClientCommandContext cmd = fsm_context;
2686 SilcClientConnection conn = cmd->conn;
2690 if (cmd->argc < 2) {
2691 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2692 "Usage: /SERVICE [<service name>] [-pubkey]");
2693 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2694 return SILC_FSM_FINISH;
2697 name = cmd->argv[1];
2699 /* Send SERVICE command to the server */
2700 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2701 ++conn->cmd_ident, 1,
2702 1, name, strlen(name));
2703 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2704 NULL, 0, NULL, NULL, buffer->data,
2706 silc_buffer_free(buffer);
2709 /* Notify application */
2710 COMMAND(SILC_STATUS_OK);
2712 /** Wait for command reply */
2713 silc_fsm_next(fsm, silc_client_command_reply_wait);
2714 return SILC_FSM_CONTINUE;
2717 /* Register all default commands provided by the client library for the
2720 void silc_client_commands_register(SilcClient client)
2722 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2725 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2726 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2727 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2728 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2729 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2730 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2731 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2732 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2733 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2734 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2735 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2736 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2737 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2738 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2739 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2740 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2741 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2742 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2743 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2744 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2745 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2746 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2747 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2748 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2749 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2750 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2751 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2754 /* Unregister all commands. */
2756 void silc_client_commands_unregister(SilcClient client)
2758 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2759 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2760 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2761 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2762 SILC_CLIENT_CMDU(list, LIST, "LIST");
2763 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2764 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2765 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2766 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2767 SILC_CLIENT_CMDU(info, INFO, "INFO");
2768 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2769 SILC_CLIENT_CMDU(ping, PING, "PING");
2770 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2771 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2772 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2773 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2774 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2775 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2776 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2777 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2778 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2779 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2780 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2781 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2782 SILC_CLIENT_CMDU(users, USERS, "USERS");
2783 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2784 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2787 /****************** Client Side Incoming Command Handling *******************/
2789 /* Reply to WHOIS command from server */
2791 static void silc_client_command_process_whois(SilcClient client,
2792 SilcClientConnection conn,
2793 SilcCommandPayload payload,
2794 SilcArgumentPayload args)
2799 SilcBuffer buffer, packet;
2801 SILC_LOG_DEBUG(("Received WHOIS command"));
2803 /* Try to take the Requested Attributes */
2804 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2808 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2812 /* Process requested attributes */
2813 buffer = silc_client_attributes_process(client, conn, attrs);
2815 silc_attribute_payload_list_free(attrs);
2819 /* Send the attributes back in COMMAND_REPLY packet */
2821 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2823 silc_command_get_ident(payload),
2824 1, 11, buffer->data,
2825 silc_buffer_len(buffer));
2827 silc_buffer_free(buffer);
2831 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
2833 silc_packet_send(conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
2834 silc_buffer_datalen(packet));
2836 silc_buffer_free(packet);
2837 silc_buffer_free(buffer);
2840 /* Client is able to receive some command packets even though they are
2841 special case. Server may send WHOIS command to the client to retrieve
2842 Requested Attributes information for WHOIS query the server is
2843 processing. This function currently handles only the WHOIS command,
2844 but if in the future more commands may arrive then this can be made
2845 to support other commands too. */
2847 SILC_FSM_STATE(silc_client_command)
2849 SilcClientConnection conn = fsm_context;
2850 SilcClient client = conn->client;
2851 SilcPacket packet = state_context;
2852 SilcCommandPayload payload;
2853 SilcCommand command;
2854 SilcArgumentPayload args;
2856 /* Get command payload from packet */
2857 payload = silc_command_payload_parse(packet->buffer.data,
2858 silc_buffer_len(&packet->buffer));
2860 SILC_LOG_DEBUG(("Bad command packet"));
2861 return SILC_FSM_FINISH;
2865 args = silc_command_get_args(payload);
2867 /* Get the command */
2868 command = silc_command_get(payload);
2871 case SILC_COMMAND_WHOIS:
2872 /* Ignore everything if requested by application */
2873 if (conn->internal->params.ignore_requested_attributes)
2876 silc_client_command_process_whois(client, conn, payload, args);
2883 silc_command_payload_free(payload);
2884 return SILC_FSM_FINISH;