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 /* Register command to client */
129 silc_client_command_register(SilcClient client,
132 SilcFSMStateCallback command_func,
133 SilcFSMStateCallback command_reply_func,
136 SilcClientCommand cmd;
138 cmd = silc_calloc(1, sizeof(*cmd));
142 cmd->command = command_func;
143 cmd->reply = command_reply_func;
144 cmd->max_args = max_args;
145 cmd->name = name ? strdup(name) : NULL;
151 silc_list_add(client->internal->commands, cmd);
156 /* Unregister command from client */
159 silc_client_command_unregister(SilcClient client,
161 SilcFSMStateCallback command_func,
162 SilcFSMStateCallback command_reply_func)
164 SilcClientCommand cmd;
166 silc_list_start(client->internal->commands);
167 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
168 if (cmd->cmd == command && cmd->command == command_func &&
169 cmd->reply == command_reply_func) {
170 silc_list_del(client->internal->commands, cmd);
171 silc_free(cmd->name);
180 /* Finds and returns a pointer to the command list. Return NULL if the
181 command is not found. */
183 static SilcClientCommand silc_client_command_find(SilcClient client,
186 SilcClientCommand cmd;
188 silc_list_start(client->internal->commands);
189 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
190 if (cmd->name && !strcasecmp(cmd->name, name))
197 /* Command thread destructor */
199 static void silc_client_command_destructor(SilcFSMThread thread,
201 void *destructor_context)
203 SilcClientCommandContext cmd = fsm_context;
204 SilcClientConnection conn = cmd->conn;
206 /* Removes commands that aren't waiting for reply but are waiting
207 for something. They may not have been removed yet. */
208 silc_list_del(conn->internal->pending_commands, cmd);
210 silc_client_command_free(cmd);
213 /* Add a command pending a command reply. Used internally by the library. */
216 silc_client_command_add_pending(SilcClientConnection conn,
217 SilcClientCommandContext cmd,
218 SilcClientCommandReply reply,
221 SilcClientCommandReplyCallback cb;
223 silc_mutex_lock(conn->internal->lock);
225 /* Add pending callback, if defined */
227 cb = silc_calloc(1, sizeof(*cb));
229 silc_mutex_unlock(conn->internal->lock);
233 cb->context = context;
234 silc_list_add(cmd->reply_callbacks, cb);
237 /* Add pending reply */
238 silc_list_add(conn->internal->pending_commands, cmd);
240 silc_mutex_unlock(conn->internal->lock);
245 /* Generic function to send any command. The arguments must be sent already
246 encoded into correct format and in correct order. Arguments come from
247 variable argument list pointer. */
249 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
250 SilcClientConnection conn,
251 SilcClientCommandContext cmd,
253 SilcClientCommandReply reply,
255 SilcUInt32 argc, va_list ap)
259 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
261 if (conn->internal->disconnected)
265 cmd->cmd_ident = silc_client_cmd_ident(conn);
267 /* Encode command payload */
268 packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
272 /* Send the command */
273 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
274 silc_buffer_datalen(packet))) {
275 silc_buffer_free(packet);
279 /* Add the command pending command reply */
280 silc_client_command_add_pending(conn, cmd, reply, reply_context);
282 silc_buffer_free(packet);
284 return cmd->cmd_ident;
287 /* Generic function to send any command. The arguments must be sent already
288 encoded into correct format and in correct order. Arguments come from
292 silc_client_command_send_arg_array(SilcClient client,
293 SilcClientConnection conn,
294 SilcClientCommandContext cmd,
296 SilcClientCommandReply reply,
299 unsigned char **argv,
300 SilcUInt32 *argv_lens,
301 SilcUInt32 *argv_types)
305 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
307 if (conn->internal->disconnected)
311 cmd->cmd_ident = silc_client_cmd_ident(conn);
313 /* Encode command payload */
314 packet = silc_command_payload_encode(command, argc, argv, argv_lens,
315 argv_types, cmd->cmd_ident);
319 /* Send the command */
320 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
321 silc_buffer_datalen(packet))) {
322 silc_buffer_free(packet);
326 /* Add the command pending command reply */
327 silc_client_command_add_pending(conn, cmd, reply, reply_context);
329 silc_buffer_free(packet);
331 return cmd->cmd_ident;
334 /* Generic function to send any command. The arguments must be sent already
335 encoded into correct format and in correct order. This is used internally
338 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
339 SilcClientCommandContext cmd,
341 SilcClientCommandReply reply,
343 SilcUInt32 argc, ...)
346 SilcUInt16 cmd_ident;
349 cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
350 reply, reply_context, argc, ap);
356 /****************************** Command API *********************************/
358 /* Free command context and its internals */
360 void silc_client_command_free(SilcClientCommandContext cmd)
362 SilcClientCommandReplyCallback cb;
365 for (i = 0; i < cmd->argc; i++)
366 silc_free(cmd->argv[i]);
367 silc_free(cmd->argv);
368 silc_free(cmd->argv_lens);
369 silc_free(cmd->argv_types);
371 silc_list_start(cmd->reply_callbacks);
372 while ((cb = silc_list_get(cmd->reply_callbacks)))
378 /* Executes a command */
380 SilcUInt16 silc_client_command_call(SilcClient client,
381 SilcClientConnection conn,
382 const char *command_line, ...)
386 unsigned char **argv = NULL;
387 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
388 SilcClientCommand command;
389 SilcClientCommandContext cmd;
393 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_ERROR,
394 "You are not connected to a server, please connect to server");
398 /* Parse arguments */
399 va_start(va, command_line);
403 /* Get command name */
404 command_name = silc_memdup(command_line, strcspn(command_line, " "));
408 /* Find command by name */
409 command = silc_client_command_find(client, command_name);
411 silc_free(command_name);
415 /* Parse command line */
416 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
417 &argv_types, &argc, command->max_args);
419 silc_free(command_name);
421 arg = va_arg(va, char *);
425 /* Find command by name */
426 command = silc_client_command_find(client, arg);
431 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
432 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
433 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
434 if (!argv || !argv_lens || !argv_types)
436 argv[argc] = silc_memdup(arg, strlen(arg));
439 argv_lens[argc] = strlen(arg);
440 argv_types[argc] = argc;
442 arg = va_arg(va, char *);
447 /* Allocate command context */
448 cmd = silc_calloc(1, sizeof(*cmd));
452 cmd->cmd = command->cmd;
455 cmd->argv_lens = argv_lens;
456 cmd->argv_types = argv_types;
457 cmd->cmd_ident = silc_client_cmd_ident(conn);
460 silc_list_init(cmd->reply_callbacks,
461 struct SilcClientCommandReplyCallbackStruct, next);
464 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
465 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
466 silc_client_command_destructor, NULL, FALSE);
467 silc_fsm_start_sync(&cmd->thread, command->command);
469 return cmd->cmd_ident;
472 /* Generic function to send any command. The arguments must be sent already
473 encoded into correct format and in correct order. */
475 SilcUInt16 silc_client_command_send(SilcClient client,
476 SilcClientConnection conn,
478 SilcClientCommandReply reply,
480 SilcUInt32 argc, ...)
482 SilcClientCommandContext cmd;
488 /* Allocate command context */
489 cmd = silc_calloc(1, sizeof(*cmd));
494 silc_list_init(cmd->reply_callbacks,
495 struct SilcClientCommandReplyCallbackStruct, next);
497 /* Send the command */
500 silc_client_command_send_vap(client, conn, cmd, command, reply,
501 reply_context, argc, ap);
504 if (!cmd->cmd_ident) {
505 silc_client_command_free(cmd);
509 /*** Wait for command reply */
510 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
511 silc_client_command_destructor, NULL, FALSE);
512 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
514 return cmd->cmd_ident;
517 /* Generic function to send any command. The arguments must be sent already
518 encoded into correct format and in correct order. Arguments come from
521 SilcUInt16 silc_client_command_send_argv(SilcClient client,
522 SilcClientConnection conn,
524 SilcClientCommandReply reply,
527 unsigned char **argv,
528 SilcUInt32 *argv_lens,
529 SilcUInt32 *argv_types)
531 SilcClientCommandContext cmd;
536 /* Allocate command context */
537 cmd = silc_calloc(1, sizeof(*cmd));
543 /* Send the command */
545 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
546 reply_context, argc, argv, argv_lens,
548 if (!cmd->cmd_ident) {
549 silc_client_command_free(cmd);
553 /*** Wait for command reply */
554 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
555 silc_client_command_destructor, NULL, FALSE);
556 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
558 return cmd->cmd_ident;
561 /* Attach to a command and command identifier to receive command reply. */
563 SilcBool silc_client_command_pending(SilcClientConnection conn,
566 SilcClientCommandReply reply,
569 SilcClientCommandContext cmd;
570 SilcClientCommandReplyCallback cb;
575 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
577 silc_mutex_lock(conn->internal->lock);
579 /* Find the pending command */
580 silc_list_start(conn->internal->pending_commands);
581 while ((cmd = silc_list_get(conn->internal->pending_commands)))
582 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
583 && cmd->cmd_ident == ident) {
585 /* Add the callback */
586 cb = silc_calloc(1, sizeof(*cb));
590 cb->context = context;
591 silc_list_add(cmd->reply_callbacks, cb);
594 silc_mutex_unlock(conn->internal->lock);
599 /******************************** WHOIS *************************************/
601 /* Command WHOIS. This command is used to query information about
604 SILC_FSM_STATE(silc_client_command_whois)
606 SilcClientCommandContext cmd = fsm_context;
607 SilcClientConnection conn = cmd->conn;
608 SilcClient client = conn->client;
609 SilcBuffer attrs = NULL;
610 unsigned char count[4], *tmp = NULL;
611 SilcBool details = FALSE, nick = FALSE;
612 unsigned char *pubkey = NULL;
615 /* Given without arguments fetches client's own information */
617 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
618 silc_buffer_data(conn->internal->local_idp),
619 silc_buffer_len(conn->internal->local_idp));
621 /* Notify application */
622 COMMAND(SILC_STATUS_OK);
624 /** Wait for command reply */
625 silc_fsm_next(fsm, silc_client_command_reply_wait);
626 return SILC_FSM_CONTINUE;
629 for (i = 1; i < cmd->argc; i++) {
630 if (!strcasecmp(cmd->argv[i], "-details")) {
632 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
633 pubkey = cmd->argv[i + 1];
636 /* We assume that the first parameter is the nickname, if it isn't
637 -details or -pubkey. The last parameter should always be the count */
640 } else if (i == cmd->argc - 1) {
641 int c = atoi(cmd->argv[i]);
642 SILC_PUT32_MSB(c, count);
649 /* If pubkey is set, add all attributes to the attrs buffer, except
652 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
653 SILC_ATTRIBUTE_SERVICE,
654 SILC_ATTRIBUTE_STATUS_MOOD,
655 SILC_ATTRIBUTE_STATUS_FREETEXT,
656 SILC_ATTRIBUTE_STATUS_MESSAGE,
657 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
658 SILC_ATTRIBUTE_PREFERRED_CONTACT,
659 SILC_ATTRIBUTE_TIMEZONE,
660 SILC_ATTRIBUTE_GEOLOCATION,
661 SILC_ATTRIBUTE_DEVICE_INFO,
662 SILC_ATTRIBUTE_USER_ICON, 0);
664 attrs = silc_client_attributes_request(0);
669 SilcAttributeObjPk obj;
672 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
673 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
674 "Could not load public key %s, check the filename",
676 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
680 switch (silc_pkcs_get_type(pk)) {
682 obj.type = "silc-rsa";
685 obj.type = "ssh-rsa";
687 case SILC_PKCS_X509V3:
688 obj.type = "x509v3-sign-rsa";
690 case SILC_PKCS_OPENPGP:
691 obj.type = "pgp-sign-rsa";
697 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
699 attrs = silc_attribute_payload_encode(attrs,
700 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
701 SILC_ATTRIBUTE_FLAG_VALID,
706 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
707 3, 1, nick ? cmd->argv[1] : NULL,
708 nick ? cmd->argv_lens[1] : 0,
709 2, tmp ? tmp : NULL, tmp ? 4 : 0,
710 3, silc_buffer_datalen(attrs));
712 /* Notify application */
713 COMMAND(SILC_STATUS_OK);
715 /** Wait for command reply */
716 silc_fsm_next(fsm, silc_client_command_reply_wait);
717 return SILC_FSM_CONTINUE;
720 return SILC_FSM_FINISH;
723 /******************************** WHOWAS ************************************/
725 /* Command WHOWAS. This command is used to query history information about
726 specific user that used to exist in the network. */
728 SILC_FSM_STATE(silc_client_command_whowas)
730 SilcClientCommandContext cmd = fsm_context;
731 SilcClientConnection conn = cmd->conn;
732 unsigned char count[4];
735 if (cmd->argc < 2 || cmd->argc > 3) {
736 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
737 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
738 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
739 SILC_STATUS_ERR_TOO_MANY_PARAMS));
740 return SILC_FSM_FINISH;
743 if (cmd->argc == 2) {
744 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
745 1, 1, cmd->argv[1], cmd->argv_lens[1]);
747 c = atoi(cmd->argv[2]);
748 SILC_PUT32_MSB(c, count);
749 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
750 2, 1, cmd->argv[1], cmd->argv_lens[1],
751 2, count, sizeof(count));
754 /* Notify application */
755 COMMAND(SILC_STATUS_OK);
757 /** Wait for command reply */
758 silc_fsm_next(fsm, silc_client_command_reply_wait);
759 return SILC_FSM_CONTINUE;
762 /******************************** IDENTIFY **********************************/
764 /* Command IDENTIFY. This command is used to query information about
765 specific user, especially ID's. */
767 SILC_FSM_STATE(silc_client_command_identify)
769 SilcClientCommandContext cmd = fsm_context;
770 SilcClientConnection conn = cmd->conn;
771 unsigned char count[4];
774 if (cmd->argc < 2 || cmd->argc > 3)
775 return SILC_FSM_FINISH;
777 if (cmd->argc == 2) {
778 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
779 1, 1, cmd->argv[1], cmd->argv_lens[1]);
781 c = atoi(cmd->argv[2]);
782 SILC_PUT32_MSB(c, count);
783 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
784 2, 1, cmd->argv[1], cmd->argv_lens[1],
785 4, count, sizeof(count));
788 /** Wait for command reply */
789 silc_fsm_next(fsm, silc_client_command_reply_wait);
790 return SILC_FSM_CONTINUE;
793 /********************************** NICK ************************************/
795 /* Command NICK. Shows current nickname/sets new nickname on current
798 SILC_FSM_STATE(silc_client_command_nick)
800 SilcClientCommandContext cmd2, cmd = fsm_context;
801 SilcClientConnection conn = cmd->conn;
804 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
805 "Usage: /NICK <nickname>");
806 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
810 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
813 /* Show current nickname */
816 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
817 "Your nickname is %s on server %s",
818 conn->local_entry->nickname, conn->remote_host);
820 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
821 "Your nickname is %s", conn->local_entry->nickname);
824 COMMAND(SILC_STATUS_OK);
828 /* If JOIN command is active, wait for it to finish before sending NICK.
829 To avoid problems locally with changing IDs while joining, we do this. */
830 silc_mutex_lock(conn->internal->lock);
831 silc_list_start(conn->internal->pending_commands);
832 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
833 if (cmd2->cmd == SILC_COMMAND_JOIN) {
834 silc_mutex_unlock(conn->internal->lock);
835 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
836 return SILC_FSM_WAIT;
839 silc_mutex_unlock(conn->internal->lock);
841 if (cmd->argv_lens[1] > 128)
842 cmd->argv_lens[1] = 128;
844 /* Send the NICK command */
845 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
846 1, 1, cmd->argv[1], cmd->argv_lens[1]);
848 /* Notify application */
849 COMMAND(SILC_STATUS_OK);
851 /** Wait for command reply */
852 silc_fsm_next(fsm, silc_client_command_reply_wait);
853 return SILC_FSM_CONTINUE;
856 return SILC_FSM_FINISH;
859 /********************************** LIST ************************************/
861 /* Command LIST. Lists channels on the current server. */
863 SILC_FSM_STATE(silc_client_command_list)
865 SilcClientCommandContext cmd = fsm_context;
866 SilcClientConnection conn = cmd->conn;
867 SilcClient client = conn->client;
868 SilcChannelEntry channel = NULL;
869 SilcBuffer idp = NULL;
871 if (cmd->argc == 2) {
872 /* Get the Channel ID of the channel */
873 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
875 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
879 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
881 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
882 1, 1, silc_buffer_datalen(idp));
884 silc_buffer_free(idp);
885 silc_client_unref_channel(client, conn, channel);
887 /* Notify application */
888 COMMAND(SILC_STATUS_OK);
890 /** Wait for command reply */
891 silc_fsm_next(fsm, silc_client_command_reply_wait);
892 return SILC_FSM_CONTINUE;
895 /********************************** TOPIC ***********************************/
897 /* Command TOPIC. Sets/shows topic on a channel. */
899 SILC_FSM_STATE(silc_client_command_topic)
901 SilcClientCommandContext cmd = fsm_context;
902 SilcClientConnection conn = cmd->conn;
903 SilcClient client = conn->client;
904 SilcChannelEntry channel;
908 if (cmd->argc < 2 || cmd->argc > 3) {
909 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
910 "Usage: /TOPIC <channel> [<topic>]");
911 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
912 SILC_STATUS_ERR_TOO_MANY_PARAMS));
916 if (cmd->argv[1][0] == '*') {
917 if (!conn->current_channel) {
918 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
921 name = conn->current_channel->channel_name;
926 if (!conn->current_channel) {
927 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
931 /* Get the Channel ID of the channel */
932 channel = silc_client_get_channel(conn->client, conn, name);
934 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
938 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
940 /* Send TOPIC command to the server */
942 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
943 1, silc_buffer_datalen(idp),
944 2, cmd->argv[2], strlen(cmd->argv[2]));
946 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
947 1, silc_buffer_datalen(idp));
949 silc_buffer_free(idp);
950 silc_client_unref_channel(client, conn, channel);
952 /* Notify application */
953 COMMAND(SILC_STATUS_OK);
955 /** Wait for command reply */
956 silc_fsm_next(fsm, silc_client_command_reply_wait);
957 return SILC_FSM_CONTINUE;
960 return SILC_FSM_FINISH;
963 /********************************* INVITE ***********************************/
965 /* Command INVITE. Invites specific client to join a channel. This is
966 also used to mange the invite list of the channel. */
968 SILC_FSM_STATE(silc_client_command_invite)
970 SilcClientCommandContext cmd = fsm_context;
971 SilcClientConnection conn = cmd->conn;
972 SilcClient client = conn->client;
973 SilcClientEntry client_entry = NULL;
974 SilcChannelEntry channel = NULL;
975 SilcBuffer clidp, chidp, args = NULL;
976 SilcPublicKey pubkey = NULL;
977 SilcDList clients = NULL;
978 char *nickname = NULL, *name;
980 unsigned char action[1];
983 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
984 "Usage: /INVITE <channel> [<nickname>[@server>]"
985 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
986 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
990 if (cmd->argv[1][0] == '*') {
991 if (!conn->current_channel) {
992 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
996 channel = conn->current_channel;
997 silc_client_ref_channel(client, conn, channel);
1001 channel = silc_client_get_channel(conn->client, conn, name);
1003 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1008 /* Parse the typed nickname. */
1009 if (cmd->argc == 3) {
1010 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1011 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1013 /* Find client entry */
1014 clients = silc_client_get_clients_local(client, conn, nickname,
1017 /* Resolve client information */
1018 SILC_FSM_CALL(silc_client_get_clients(
1019 client, conn, nickname,
1021 silc_client_command_resolve_continue,
1024 client_entry = silc_dlist_get(clients);
1026 if (cmd->argv[2][0] == '+')
1031 /* Check if it is public key file to be added to invite list */
1032 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1033 invite = cmd->argv[2];
1040 args = silc_buffer_alloc_size(2);
1041 silc_buffer_format(args,
1042 SILC_STR_UI_SHORT(1),
1045 chidp = silc_public_key_payload_encode(pubkey);
1046 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1047 silc_buffer_len(chidp), 2);
1048 silc_buffer_free(chidp);
1049 silc_pkcs_public_key_free(pubkey);
1051 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1055 /* Send the command */
1056 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1058 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1059 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1060 1, silc_buffer_datalen(chidp),
1061 2, silc_buffer_datalen(clidp),
1062 3, args ? action : NULL, args ? 1 : 0,
1063 4, silc_buffer_datalen(args));
1064 silc_buffer_free(clidp);
1066 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1067 1, silc_buffer_datalen(chidp),
1068 3, args ? action : NULL, args ? 1 : 0,
1069 4, silc_buffer_datalen(args));
1072 silc_buffer_free(chidp);
1073 silc_buffer_free(args);
1074 silc_free(nickname);
1075 silc_client_list_free(client, conn, clients);
1076 silc_client_unref_channel(client, conn, channel);
1078 /* Notify application */
1079 COMMAND(SILC_STATUS_OK);
1081 /** Wait for command reply */
1082 silc_fsm_next(fsm, silc_client_command_reply_wait);
1083 return SILC_FSM_CONTINUE;
1086 silc_free(nickname);
1087 return SILC_FSM_FINISH;
1090 /********************************** QUIT ************************************/
1092 /* Close the connection */
1094 SILC_FSM_STATE(silc_client_command_quit_final)
1096 SilcClientCommandContext cmd = fsm_context;
1097 SilcClientConnection conn = cmd->conn;
1099 SILC_LOG_DEBUG(("Quitting"));
1101 /* Notify application */
1102 COMMAND(SILC_STATUS_OK);
1104 /* Signal to close connection */
1105 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1106 if (!conn->internal->disconnected) {
1107 conn->internal->disconnected = TRUE;
1108 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1111 return SILC_FSM_FINISH;
1114 /* Command QUIT. Closes connection with current server. */
1116 SILC_FSM_STATE(silc_client_command_quit)
1118 SilcClientCommandContext cmd = fsm_context;
1119 SilcClientConnection conn = cmd->conn;
1122 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1123 1, cmd->argv[1], cmd->argv_lens[1]);
1125 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1127 /* Sleep for a while */
1130 /* We close the connection with a little timeout */
1131 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1132 return SILC_FSM_WAIT;
1135 /********************************** KILL ************************************/
1137 /* Command KILL. Router operator can use this command to remove an client
1138 fromthe SILC Network. */
1140 SILC_FSM_STATE(silc_client_command_kill)
1142 SilcClientCommandContext cmd = fsm_context;
1143 SilcClientConnection conn = cmd->conn;
1144 SilcClient client = conn->client;
1145 SilcBuffer idp, auth = NULL;
1146 SilcClientEntry target;
1148 char *nickname = NULL, *comment = NULL;
1150 if (cmd->argc < 2) {
1151 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1152 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1153 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1154 return SILC_FSM_FINISH;
1157 /* Parse the typed nickname. */
1158 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1159 return SILC_FSM_FINISH;
1161 /* Get the target client */
1162 clients = silc_client_get_clients_local(client, conn, nickname,
1165 /* Resolve client information */
1166 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
1168 silc_client_command_resolve_continue,
1171 target = silc_dlist_get(clients);
1173 if (cmd->argc >= 3) {
1174 if (strcasecmp(cmd->argv[2], "-pubkey"))
1175 comment = cmd->argv[2];
1177 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1178 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1179 /* Encode the public key authentication payload */
1180 auth = silc_auth_public_key_auth_generate(conn->public_key,
1183 conn->internal->sha1hash,
1184 &target->id, SILC_ID_CLIENT);
1188 /* Send the KILL command to the server */
1189 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1190 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1191 1, silc_buffer_datalen(idp),
1192 2, comment, comment ? strlen(comment) : 0,
1193 3, silc_buffer_datalen(auth));
1194 silc_buffer_free(idp);
1195 silc_buffer_free(auth);
1196 silc_free(nickname);
1197 silc_client_list_free(client, conn, clients);
1199 /* Notify application */
1200 COMMAND(SILC_STATUS_OK);
1202 /** Wait for command reply */
1203 silc_fsm_next(fsm, silc_client_command_reply_wait);
1204 return SILC_FSM_CONTINUE;
1207 /********************************** INFO ************************************/
1209 /* Command INFO. Request information about specific server. If specific
1210 server is not provided the current server is used. */
1212 SILC_FSM_STATE(silc_client_command_info)
1214 SilcClientCommandContext cmd = fsm_context;
1215 SilcClientConnection conn = cmd->conn;
1217 /* Send the command */
1219 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1220 1, cmd->argv[1], cmd->argv_lens[1]);
1222 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1224 /* Notify application */
1225 COMMAND(SILC_STATUS_OK);
1227 /** Wait for command reply */
1228 silc_fsm_next(fsm, silc_client_command_reply_wait);
1229 return SILC_FSM_CONTINUE;
1232 /********************************** STATS ***********************************/
1234 /* Command STATS. Shows server and network statistics. */
1236 SILC_FSM_STATE(silc_client_command_stats)
1238 SilcClientCommandContext cmd = fsm_context;
1239 SilcClientConnection conn = cmd->conn;
1241 /* Send the command */
1242 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1243 1, silc_buffer_datalen(conn->internal->
1246 /* Notify application */
1247 COMMAND(SILC_STATUS_OK);
1249 /** Wait for command reply */
1250 silc_fsm_next(fsm, silc_client_command_reply_wait);
1251 return SILC_FSM_CONTINUE;
1254 /********************************** PING ************************************/
1256 /* Command PING. Sends ping to server. */
1258 SILC_FSM_STATE(silc_client_command_ping)
1260 SilcClientCommandContext cmd = fsm_context;
1261 SilcClientConnection conn = cmd->conn;
1263 if (cmd->argc < 2) {
1264 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1265 return SILC_FSM_FINISH;
1268 /* Send the command */
1269 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1270 1, silc_buffer_datalen(conn->internal->
1273 /* Save ping time */
1274 cmd->context = SILC_64_TO_PTR(silc_time());
1276 /* Notify application */
1277 COMMAND(SILC_STATUS_OK);
1279 /** Wait for command reply */
1280 silc_fsm_next(fsm, silc_client_command_reply_wait);
1281 return SILC_FSM_CONTINUE;
1284 /********************************** JOIN ************************************/
1286 /* Command JOIN. Joins to a channel. */
1288 SILC_FSM_STATE(silc_client_command_join)
1290 SilcClientCommandContext cmd2, cmd = fsm_context;
1291 SilcClientConnection conn = cmd->conn;
1292 SilcClient client = conn->client;
1293 SilcChannelEntry channel = NULL;
1294 SilcBuffer auth = NULL, cauth = NULL;
1295 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1296 int i, passphrase_len = 0;
1298 if (cmd->argc < 2) {
1299 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1303 /* See if we have joined to the requested channel already */
1304 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1305 if (channel && silc_client_on_channel(channel, conn->local_entry))
1308 /* If NICK command is active, wait for it to finish before sending JOIN.
1309 To avoid problems locally with changing IDs while joining, we do this. */
1310 silc_mutex_lock(conn->internal->lock);
1311 silc_list_start(conn->internal->pending_commands);
1312 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1313 if (cmd2->cmd == SILC_COMMAND_NICK) {
1314 silc_mutex_unlock(conn->internal->lock);
1315 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1316 return SILC_FSM_WAIT;
1319 silc_mutex_unlock(conn->internal->lock);
1321 if (cmd->argv_lens[1] > 256)
1322 cmd->argv_lens[1] = 256;
1324 name = cmd->argv[1];
1326 for (i = 2; i < cmd->argc; i++) {
1327 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1328 cipher = cmd->argv[++i];
1329 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1330 hmac = cmd->argv[++i];
1331 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1332 auth = silc_auth_public_key_auth_generate(conn->public_key,
1335 conn->internal->sha1hash,
1338 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1339 SilcPublicKey pubkey = conn->public_key;
1340 SilcPrivateKey privkey = conn->private_key;
1341 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1344 if (cmd->argc >= i + 3) {
1346 if (cmd->argc >= i + 4) {
1347 pass = cmd->argv[i + 3];
1350 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1351 &pubkey, &privkey)) {
1352 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1353 "Could not load key pair, check your arguments");
1354 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1360 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1361 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1363 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1364 memcpy(pubdata, pkhash, 20);
1365 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1367 conn->internal->sha1hash,
1370 memset(pubdata, 0, 128);
1373 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1374 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1375 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1376 cmd->argv_lens[i], 0);
1377 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1378 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1379 0, pu8, passphrase_len);
1382 passphrase = strdup(cmd->argv[i]);
1383 passphrase_len = cmd->argv_lens[i];
1388 /* Send JOIN command to the server */
1389 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1390 1, name, strlen(name),
1391 2, silc_buffer_datalen(conn->internal->
1393 3, passphrase, passphrase_len,
1394 4, cipher, cipher ? strlen(cipher) : 0,
1395 5, hmac, hmac ? strlen(hmac) : 0,
1396 6, silc_buffer_datalen(auth),
1397 7, silc_buffer_datalen(cauth));
1399 silc_buffer_free(auth);
1400 silc_buffer_free(cauth);
1402 memset(passphrase, 0, strlen(passphrase));
1403 silc_free(passphrase);
1404 silc_client_unref_channel(client, conn, channel);
1406 /* Notify application */
1407 COMMAND(SILC_STATUS_OK);
1409 /** Wait for command reply */
1410 silc_fsm_next(fsm, silc_client_command_reply_wait);
1411 return SILC_FSM_CONTINUE;
1414 silc_client_unref_channel(client, conn, channel);
1415 return SILC_FSM_FINISH;
1418 /********************************** MOTD ************************************/
1420 /* MOTD command. Requests motd from server. */
1422 SILC_FSM_STATE(silc_client_command_motd)
1424 SilcClientCommandContext cmd = fsm_context;
1425 SilcClientConnection conn = cmd->conn;
1427 if (cmd->argc < 1 || cmd->argc > 2) {
1428 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1429 "Usage: /MOTD [<server>]");
1430 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1431 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1432 return SILC_FSM_FINISH;
1435 /* Send the command */
1437 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1438 1, conn->remote_host,
1439 strlen(conn->remote_host));
1441 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1442 1, cmd->argv[1], cmd->argv_lens[1]);
1444 /* Notify application */
1445 COMMAND(SILC_STATUS_OK);
1447 /** Wait for command reply */
1448 silc_fsm_next(fsm, silc_client_command_reply_wait);
1449 return SILC_FSM_CONTINUE;
1452 /********************************** UMODE ***********************************/
1454 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1455 modes as client cannot set itself server/router operator privileges. */
1457 SILC_FSM_STATE(silc_client_command_umode)
1459 SilcClientCommandContext cmd = fsm_context;
1460 SilcClientConnection conn = cmd->conn;
1461 unsigned char *cp, modebuf[4];
1462 SilcUInt32 mode, add, len;
1465 if (cmd->argc < 2) {
1466 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1467 "Usage: /UMODE +|-<modes>");
1468 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1469 return SILC_FSM_FINISH;
1472 mode = conn->local_entry->mode;
1474 /* Are we adding or removing mode */
1475 if (cmd->argv[1][0] == '-')
1481 cp = cmd->argv[1] + 1;
1483 for (i = 0; i < len; i++) {
1488 mode |= SILC_UMODE_SERVER_OPERATOR;
1489 mode |= SILC_UMODE_ROUTER_OPERATOR;
1490 mode |= SILC_UMODE_GONE;
1491 mode |= SILC_UMODE_INDISPOSED;
1492 mode |= SILC_UMODE_BUSY;
1493 mode |= SILC_UMODE_PAGE;
1494 mode |= SILC_UMODE_HYPER;
1495 mode |= SILC_UMODE_ROBOT;
1496 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1497 mode |= SILC_UMODE_REJECT_WATCHING;
1499 mode = SILC_UMODE_NONE;
1504 mode |= SILC_UMODE_SERVER_OPERATOR;
1506 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1510 mode |= SILC_UMODE_ROUTER_OPERATOR;
1512 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1516 mode |= SILC_UMODE_GONE;
1518 mode &= ~SILC_UMODE_GONE;
1522 mode |= SILC_UMODE_INDISPOSED;
1524 mode &= ~SILC_UMODE_INDISPOSED;
1528 mode |= SILC_UMODE_BUSY;
1530 mode &= ~SILC_UMODE_BUSY;
1534 mode |= SILC_UMODE_PAGE;
1536 mode &= ~SILC_UMODE_PAGE;
1540 mode |= SILC_UMODE_HYPER;
1542 mode &= ~SILC_UMODE_HYPER;
1546 mode |= SILC_UMODE_ROBOT;
1548 mode &= ~SILC_UMODE_ROBOT;
1552 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1554 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1558 mode |= SILC_UMODE_REJECT_WATCHING;
1560 mode &= ~SILC_UMODE_REJECT_WATCHING;
1564 mode |= SILC_UMODE_BLOCK_INVITE;
1566 mode &= ~SILC_UMODE_BLOCK_INVITE;
1569 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1570 return SILC_FSM_FINISH;
1575 SILC_PUT32_MSB(mode, modebuf);
1577 /* Send the command */
1578 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1579 1, silc_buffer_datalen(conn->internal->
1581 2, modebuf, sizeof(modebuf));
1583 /* Notify application */
1584 COMMAND(SILC_STATUS_OK);
1586 /** Wait for command reply */
1587 silc_fsm_next(fsm, silc_client_command_reply_wait);
1588 return SILC_FSM_CONTINUE;
1591 /********************************** CMODE ***********************************/
1593 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1594 can be set several at once. Those modes that require argument must be set
1595 separately (unless set with modes that does not require arguments). */
1597 SILC_FSM_STATE(silc_client_command_cmode)
1599 SilcClientCommandContext cmd = fsm_context;
1600 SilcClientConnection conn = cmd->conn;
1601 SilcClient client = conn->client;
1602 SilcChannelEntry channel = NULL;
1603 SilcBuffer chidp, auth = NULL, pk = NULL;
1604 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1605 SilcUInt32 mode, add, type, len, arg_len = 0;
1608 if (cmd->argc < 3) {
1609 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1610 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1611 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1615 if (cmd->argv[1][0] == '*') {
1616 if (!conn->current_channel) {
1617 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1621 channel = conn->current_channel;
1622 silc_client_ref_channel(client, conn, channel);
1624 name = cmd->argv[1];
1626 channel = silc_client_get_channel(conn->client, conn, name);
1628 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1633 mode = channel->mode;
1635 /* Are we adding or removing mode */
1636 if (cmd->argv[2][0] == '-')
1641 /* Argument type to be sent to server */
1645 cp = cmd->argv[2] + 1;
1647 for (i = 0; i < len; i++) {
1651 mode |= SILC_CHANNEL_MODE_PRIVATE;
1653 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1657 mode |= SILC_CHANNEL_MODE_SECRET;
1659 mode &= ~SILC_CHANNEL_MODE_SECRET;
1663 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1665 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1669 mode |= SILC_CHANNEL_MODE_INVITE;
1671 mode &= ~SILC_CHANNEL_MODE_INVITE;
1675 mode |= SILC_CHANNEL_MODE_TOPIC;
1677 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1681 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1683 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1687 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1689 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1694 mode |= SILC_CHANNEL_MODE_ULIMIT;
1696 if (cmd->argc < 4) {
1697 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1698 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1699 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1702 ll = atoi(cmd->argv[3]);
1703 SILC_PUT32_MSB(ll, tmp);
1707 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1712 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1714 if (cmd->argc < 4) {
1715 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1716 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1717 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1721 arg_len = cmd->argv_lens[3];
1723 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1728 mode |= SILC_CHANNEL_MODE_CIPHER;
1730 if (cmd->argc < 4) {
1731 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1732 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1733 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1737 arg_len = cmd->argv_lens[3];
1739 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1744 mode |= SILC_CHANNEL_MODE_HMAC;
1746 if (cmd->argc < 4) {
1747 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1748 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1749 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1753 arg_len = cmd->argv_lens[3];
1755 mode &= ~SILC_CHANNEL_MODE_HMAC;
1760 SilcPublicKey pubkey = conn->public_key;
1761 SilcPrivateKey privkey = conn->private_key;
1763 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1766 if (cmd->argc >= 5) {
1769 pass = cmd->argv[5];
1770 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1771 &pubkey, &privkey)) {
1772 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1773 "Could not load key pair, check your arguments");
1774 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1779 pk = silc_public_key_payload_encode(pubkey);
1780 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1782 conn->internal->sha1hash,
1785 arg = silc_buffer_data(auth);
1786 arg_len = silc_buffer_len(auth);
1788 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1794 SilcBool chadd = FALSE;
1795 SilcPublicKey chpk = NULL;
1797 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1800 if (cmd->argc == 3) {
1801 /* Send empty command to receive the public key list. */
1802 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1803 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1805 1, silc_buffer_datalen(chidp));
1806 silc_buffer_free(chidp);
1808 /* Notify application */
1809 COMMAND(SILC_STATUS_OK);
1813 if (cmd->argc >= 4) {
1814 auth = silc_buffer_alloc_size(2);
1815 silc_buffer_format(auth,
1816 SILC_STR_UI_SHORT(cmd->argc - 3),
1820 for (k = 3; k < cmd->argc; k++) {
1821 if (cmd->argv[k][0] == '+')
1823 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1824 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1825 "Could not load public key %s, check the filename",
1827 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1828 silc_buffer_free(auth);
1833 pk = silc_public_key_payload_encode(chpk);
1834 auth = silc_argument_payload_encode_one(auth,
1835 silc_buffer_datalen(pk),
1836 chadd ? 0x00 : 0x01);
1837 silc_pkcs_public_key_free(chpk);
1838 silc_buffer_free(pk);
1843 arg = silc_buffer_data(auth);
1844 arg_len = silc_buffer_len(auth);
1846 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1850 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1856 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1857 SILC_PUT32_MSB(mode, modebuf);
1859 /* Send the command. We support sending only one mode at once that
1860 requires an argument. */
1862 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1863 1, silc_buffer_datalen(chidp),
1864 2, modebuf, sizeof(modebuf),
1866 8, silc_buffer_datalen(pk));
1868 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1869 1, silc_buffer_datalen(chidp),
1870 2, modebuf, sizeof(modebuf));
1873 silc_buffer_free(chidp);
1874 silc_buffer_free(auth);
1875 silc_buffer_free(pk);
1876 silc_client_unref_channel(client, conn, channel);
1878 /* Notify application */
1879 COMMAND(SILC_STATUS_OK);
1881 /** Wait for command reply */
1882 silc_fsm_next(fsm, silc_client_command_reply_wait);
1883 return SILC_FSM_CONTINUE;
1886 silc_client_unref_channel(client, conn, channel);
1887 return SILC_FSM_FINISH;
1890 /********************************* CUMODE ***********************************/
1892 /* CUMODE command. Changes client's mode on a channel. */
1894 SILC_FSM_STATE(silc_client_command_cumode)
1896 SilcClientCommandContext cmd = fsm_context;
1897 SilcClientConnection conn = cmd->conn;
1898 SilcClient client = conn->client;
1899 SilcChannelEntry channel = NULL;
1900 SilcChannelUser chu;
1901 SilcClientEntry client_entry;
1902 SilcBuffer clidp, chidp, auth = NULL;
1903 SilcDList clients = NULL;
1904 unsigned char *name, *cp, modebuf[4];
1905 SilcUInt32 mode = 0, add, len;
1906 char *nickname = NULL;
1909 if (cmd->argc < 4) {
1910 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1911 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1912 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1916 if (cmd->argv[1][0] == '*') {
1917 if (!conn->current_channel) {
1918 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1922 channel = conn->current_channel;
1923 silc_client_ref_channel(client, conn, channel);
1925 name = cmd->argv[1];
1927 channel = silc_client_get_channel(conn->client, conn, name);
1929 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1934 /* Parse the typed nickname. */
1935 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1937 /* Find client entry */
1938 clients = silc_client_get_clients_local(client, conn, nickname,
1941 /* Resolve client information */
1942 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1943 silc_client_command_resolve_continue,
1946 client_entry = silc_dlist_get(clients);
1948 /* Get the current mode */
1949 chu = silc_client_on_channel(channel, client_entry);
1953 /* Are we adding or removing mode */
1954 if (cmd->argv[2][0] == '-')
1960 cp = cmd->argv[2] + 1;
1962 for (i = 0; i < len; i++) {
1966 mode |= SILC_CHANNEL_UMODE_CHANFO;
1967 mode |= SILC_CHANNEL_UMODE_CHANOP;
1968 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1969 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1970 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1972 mode = SILC_CHANNEL_UMODE_NONE;
1977 SilcPublicKey pubkey = conn->public_key;
1978 SilcPrivateKey privkey = conn->private_key;
1980 if (cmd->argc >= 6) {
1983 pass = cmd->argv[6];
1984 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1985 &pubkey, &privkey)) {
1986 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1987 "Could not load key pair, check your arguments");
1988 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1993 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1995 conn->internal->sha1hash,
1998 mode |= SILC_CHANNEL_UMODE_CHANFO;
2000 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2005 mode |= SILC_CHANNEL_UMODE_CHANOP;
2007 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2011 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2013 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2017 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2019 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2023 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2025 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2029 mode |= SILC_CHANNEL_UMODE_QUIET;
2031 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2034 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2040 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2041 SILC_PUT32_MSB(mode, modebuf);
2042 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2044 /* Send the command packet. We support sending only one mode at once
2045 that requires an argument. */
2046 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2047 1, silc_buffer_datalen(chidp),
2049 3, silc_buffer_datalen(clidp),
2050 4, silc_buffer_datalen(auth));
2052 silc_buffer_free(chidp);
2053 silc_buffer_free(clidp);
2055 silc_buffer_free(auth);
2056 silc_free(nickname);
2057 silc_client_list_free(client, conn, clients);
2058 silc_client_unref_channel(client, conn, channel);
2060 /* Notify application */
2061 COMMAND(SILC_STATUS_OK);
2063 /** Wait for command reply */
2064 silc_fsm_next(fsm, silc_client_command_reply_wait);
2065 return SILC_FSM_CONTINUE;
2068 silc_client_unref_channel(client, conn, channel);
2069 silc_client_list_free(client, conn, clients);
2070 silc_free(nickname);
2071 return SILC_FSM_FINISH;
2074 /********************************** KICK ************************************/
2076 /* KICK command. Kicks a client out of channel. */
2078 SILC_FSM_STATE(silc_client_command_kick)
2080 SilcClientCommandContext cmd = fsm_context;
2081 SilcClientConnection conn = cmd->conn;
2082 SilcClient client = conn->client;
2083 SilcChannelEntry channel = NULL;
2084 SilcBuffer idp, idp2;
2085 SilcClientEntry target;
2086 SilcDList clients = NULL;
2088 char *nickname = NULL;
2090 if (cmd->argc < 3) {
2091 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2092 "Usage: /KICK <channel> <nickname> [<comment>]");
2093 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2097 if (cmd->argv[1][0] == '*') {
2098 if (!conn->current_channel) {
2099 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2102 name = conn->current_channel->channel_name;
2104 name = cmd->argv[1];
2107 if (!conn->current_channel) {
2108 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2112 /* Get the Channel ID of the channel */
2113 channel = silc_client_get_channel(conn->client, conn, name);
2115 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2119 /* Parse the typed nickname. */
2120 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
2122 /* Get the target client */
2123 clients = silc_client_get_clients_local(client, conn, nickname,
2126 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2127 "No such client: %s", cmd->argv[2]);
2128 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2131 target = silc_dlist_get(clients);
2133 /* Send KICK command to the server */
2134 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2135 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2137 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2138 1, silc_buffer_datalen(idp),
2139 2, silc_buffer_datalen(idp2));
2141 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2142 1, silc_buffer_datalen(idp),
2143 2, silc_buffer_datalen(idp2),
2144 3, cmd->argv[3], strlen(cmd->argv[3]));
2146 silc_buffer_free(idp);
2147 silc_buffer_free(idp2);
2148 silc_free(nickname);
2149 silc_client_list_free(client, conn, clients);
2150 silc_client_unref_channel(client, conn, channel);
2152 /* Notify application */
2153 COMMAND(SILC_STATUS_OK);
2155 /** Wait for command reply */
2156 silc_fsm_next(fsm, silc_client_command_reply_wait);
2157 return SILC_FSM_CONTINUE;
2160 silc_client_unref_channel(client, conn, channel);
2161 silc_free(nickname);
2162 return SILC_FSM_FINISH;
2165 /***************************** OPER & SILCOPER ******************************/
2168 unsigned char *passphrase;
2169 SilcUInt32 passphrase_len;
2170 } *SilcClientCommandOper;
2172 /* Ask passphrase callback */
2174 static void silc_client_command_oper_cb(unsigned char *data,
2175 SilcUInt32 data_len, void *context)
2177 SilcClientCommandContext cmd = context;
2178 SilcClientCommandOper oper = cmd->context;
2180 if (data && data_len)
2181 oper->passphrase = silc_memdup(data, data_len);
2182 oper->passphrase_len = data_len;
2185 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2188 /* Send OPER/SILCOPER command */
2190 SILC_FSM_STATE(silc_client_command_oper_send)
2192 SilcClientCommandContext cmd = fsm_context;
2193 SilcClientConnection conn = cmd->conn;
2194 SilcClientCommandOper oper = cmd->context;
2197 if (!oper || !oper->passphrase) {
2198 /* Encode the public key authentication payload */
2199 auth = silc_auth_public_key_auth_generate(conn->public_key,
2202 conn->internal->hash,
2206 /* Encode the password authentication payload */
2207 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2208 oper->passphrase, oper->passphrase_len);
2211 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2212 1, cmd->argv[1], strlen(cmd->argv[1]),
2213 2, silc_buffer_datalen(auth));
2215 silc_buffer_clear(auth);
2216 silc_buffer_free(auth);
2218 silc_free(oper->passphrase);
2222 /* Notify application */
2223 COMMAND(SILC_STATUS_OK);
2225 /** Wait for command reply */
2226 silc_fsm_next(fsm, silc_client_command_reply_wait);
2227 return SILC_FSM_CONTINUE;
2230 /* OPER command. Used to obtain server operator privileges. */
2232 SILC_FSM_STATE(silc_client_command_oper)
2234 SilcClientCommandContext cmd = fsm_context;
2235 SilcClientConnection conn = cmd->conn;
2236 SilcClientCommandOper oper;
2238 if (cmd->argc < 2) {
2239 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2240 "Usage: /OPER <username> [-pubkey]");
2241 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2242 return SILC_FSM_FINISH;
2245 /* Get passphrase */
2246 if (cmd->argc < 3) {
2247 oper = silc_calloc(1, sizeof(*oper));
2249 return SILC_FSM_FINISH;
2250 cmd->context = oper;
2251 SILC_FSM_CALL(conn->client->internal->
2252 ops->ask_passphrase(conn->client, conn,
2253 silc_client_command_oper_cb, cmd));
2256 silc_fsm_next(fsm, silc_client_command_oper_send);
2257 return SILC_FSM_CONTINUE;
2260 /* SILCOPER command. Used to obtain router operator privileges. */
2262 SILC_FSM_STATE(silc_client_command_silcoper)
2264 SilcClientCommandContext cmd = fsm_context;
2265 SilcClientConnection conn = cmd->conn;
2266 SilcClientCommandOper oper;
2268 if (cmd->argc < 2) {
2269 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2270 "Usage: /SILCOPER <username> [-pubkey]");
2271 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2272 return SILC_FSM_FINISH;
2275 /* Get passphrase */
2276 if (cmd->argc < 3) {
2277 oper = silc_calloc(1, sizeof(*oper));
2279 return SILC_FSM_FINISH;
2280 cmd->context = oper;
2281 SILC_FSM_CALL(conn->client->internal->
2282 ops->ask_passphrase(conn->client, conn,
2283 silc_client_command_oper_cb, cmd));
2286 silc_fsm_next(fsm, silc_client_command_oper_send);
2287 return SILC_FSM_CONTINUE;
2290 /*********************************** BAN ************************************/
2292 /* Command BAN. This is used to manage the ban list of the channel. */
2294 SILC_FSM_STATE(silc_client_command_ban)
2296 SilcClientCommandContext cmd = fsm_context;
2297 SilcClientConnection conn = cmd->conn;
2298 SilcClient client = conn->client;
2299 SilcChannelEntry channel;
2300 SilcBuffer chidp, args = NULL;
2301 char *name, *ban = NULL;
2302 unsigned char action[1];
2303 SilcPublicKey pubkey = NULL;
2305 if (cmd->argc < 2) {
2306 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2307 "Usage: /BAN <channel> "
2308 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2309 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2313 if (cmd->argv[1][0] == '*') {
2314 if (!conn->current_channel) {
2315 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2319 channel = conn->current_channel;
2320 silc_client_ref_channel(client, conn, channel);
2322 name = cmd->argv[1];
2324 channel = silc_client_get_channel(conn->client, conn, name);
2326 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2331 if (cmd->argc == 3) {
2332 if (cmd->argv[2][0] == '+')
2337 /* Check if it is public key file to be added to invite list */
2338 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2345 args = silc_buffer_alloc_size(2);
2346 silc_buffer_format(args,
2347 SILC_STR_UI_SHORT(1),
2350 chidp = silc_public_key_payload_encode(pubkey);
2351 args = silc_argument_payload_encode_one(args,
2352 silc_buffer_datalen(chidp), 2);
2353 silc_buffer_free(chidp);
2354 silc_pkcs_public_key_free(pubkey);
2356 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2360 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2362 /* Send the command */
2363 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2364 1, silc_buffer_datalen(chidp),
2365 2, args ? action : NULL, args ? 1 : 0,
2366 3, silc_buffer_datalen(args));
2368 silc_buffer_free(chidp);
2369 silc_buffer_free(args);
2370 silc_client_unref_channel(client, conn, channel);
2372 /* Notify application */
2373 COMMAND(SILC_STATUS_OK);
2375 /** Wait for command reply */
2376 silc_fsm_next(fsm, silc_client_command_reply_wait);
2377 return SILC_FSM_CONTINUE;
2380 return SILC_FSM_FINISH;
2383 /********************************* DETACH ***********************************/
2385 /* Command DETACH. This is used to detach from the server */
2387 SILC_FSM_STATE(silc_client_command_detach)
2389 SilcClientCommandContext cmd = fsm_context;
2390 SilcClientConnection conn = cmd->conn;
2392 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2394 /* Notify application */
2395 COMMAND(SILC_STATUS_OK);
2397 /** Wait for command reply */
2398 silc_fsm_next(fsm, silc_client_command_reply_wait);
2399 return SILC_FSM_CONTINUE;
2402 /********************************** WATCH ***********************************/
2404 /* Command WATCH. */
2406 SILC_FSM_STATE(silc_client_command_watch)
2408 SilcClientCommandContext cmd = fsm_context;
2409 SilcClientConnection conn = cmd->conn;
2410 SilcBuffer args = NULL;
2412 const char *pubkey = NULL;
2413 SilcBool pubkey_add = TRUE;
2415 if (cmd->argc < 3) {
2416 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2420 if (!strcasecmp(cmd->argv[1], "-add")) {
2422 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2424 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2426 pubkey = cmd->argv[2] + 1;
2427 if (cmd->argv[2][0] == '-')
2430 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2438 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2439 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2440 "Could not load public key %s, check the filename", pubkey);
2441 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2445 args = silc_buffer_alloc_size(2);
2446 silc_buffer_format(args,
2447 SILC_STR_UI_SHORT(1),
2449 buffer = silc_public_key_payload_encode(pk);
2450 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2451 pubkey_add ? 0x00 : 0x01);
2452 silc_buffer_free(buffer);
2453 silc_pkcs_public_key_free(pk);
2456 /* Send the commmand */
2457 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2458 1, silc_buffer_datalen(conn->internal->
2460 type, pubkey ? args->data : cmd->argv[2],
2461 pubkey ? silc_buffer_len(args) :
2464 silc_buffer_free(args);
2466 /* Notify application */
2467 COMMAND(SILC_STATUS_OK);
2469 /** Wait for command reply */
2470 silc_fsm_next(fsm, silc_client_command_reply_wait);
2471 return SILC_FSM_CONTINUE;
2474 return SILC_FSM_FINISH;
2477 /********************************** LEAVE ***********************************/
2479 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2481 SILC_FSM_STATE(silc_client_command_leave)
2483 SilcClientCommandContext cmd = fsm_context;
2484 SilcClientConnection conn = cmd->conn;
2485 SilcClient client = conn->client;
2486 SilcChannelEntry channel;
2490 if (cmd->argc != 2) {
2491 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2492 "Usage: /LEAVE <channel>");
2493 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2497 if (cmd->argv[1][0] == '*') {
2498 if (!conn->current_channel) {
2499 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2502 name = conn->current_channel->channel_name;
2504 name = cmd->argv[1];
2507 /* Get the channel entry */
2508 channel = silc_client_get_channel(conn->client, conn, name);
2510 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2514 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2516 /* Send LEAVE command to the server */
2517 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2518 1, silc_buffer_datalen(idp));
2520 silc_buffer_free(idp);
2522 /* Notify application */
2523 COMMAND(SILC_STATUS_OK);
2525 if (conn->current_channel == channel)
2526 conn->current_channel = NULL;
2528 silc_client_unref_channel(client, conn, channel);
2530 /** Wait for command reply */
2531 silc_fsm_next(fsm, silc_client_command_reply_wait);
2532 return SILC_FSM_CONTINUE;
2535 return SILC_FSM_FINISH;
2538 /********************************** USERS ***********************************/
2540 /* Command USERS. Requests the USERS of the clients joined on requested
2543 SILC_FSM_STATE(silc_client_command_users)
2545 SilcClientCommandContext cmd = fsm_context;
2546 SilcClientConnection conn = cmd->conn;
2549 if (cmd->argc != 2) {
2550 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2551 "Usage: /USERS <channel>");
2552 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2556 if (cmd->argv[1][0] == '*') {
2557 if (!conn->current_channel) {
2558 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2561 name = conn->current_channel->channel_name;
2563 name = cmd->argv[1];
2566 /* Send USERS command to the server */
2567 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2568 2, name, strlen(name));
2570 /* Notify application */
2571 COMMAND(SILC_STATUS_OK);
2573 /** Wait for command reply */
2574 silc_fsm_next(fsm, silc_client_command_reply_wait);
2575 return SILC_FSM_CONTINUE;
2578 return SILC_FSM_FINISH;
2581 /********************************* GETKEY ***********************************/
2583 /* Command GETKEY. Used to fetch remote client's public key. */
2585 SILC_FSM_STATE(silc_client_command_getkey)
2587 SilcClientCommandContext cmd = fsm_context;
2588 SilcClientConnection conn = cmd->conn;
2589 SilcClient client = conn->client;
2590 SilcClientEntry client_entry;
2591 SilcServerEntry server_entry;
2593 char *nickname = NULL;
2596 if (cmd->argc < 2) {
2597 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2598 "Usage: /GETKEY <nickname or server name>");
2599 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2600 return SILC_FSM_FINISH;
2603 /* Parse the typed nickname. */
2604 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname)) {
2605 COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2606 return SILC_FSM_FINISH;
2609 /* Find client entry */
2610 clients = silc_client_get_clients_local(client, conn, nickname,
2613 /* Check whether user requested server */
2614 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2615 if (!server_entry) {
2616 if (cmd->resolved) {
2617 /* Resolving didn't find anything. We should never get here as
2618 errors are handled in the resolving callback. */
2619 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2620 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2621 return SILC_FSM_FINISH;
2624 /* No client or server exist with this name, query for both. */
2625 cmd->resolved = TRUE;
2626 SILC_FSM_CALL(silc_client_command_send(client, conn,
2627 SILC_COMMAND_IDENTIFY,
2628 silc_client_command_continue,
2631 strlen(cmd->argv[1]),
2633 strlen(cmd->argv[1])));
2636 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2637 silc_client_unref_server(client, conn, server_entry);
2639 client_entry = silc_dlist_get(clients);
2640 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2641 silc_client_list_free(client, conn, clients);
2644 /* Send the commmand */
2645 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2646 1, silc_buffer_datalen(idp));
2648 silc_buffer_free(idp);
2649 silc_free(nickname);
2651 /* Notify application */
2652 COMMAND(SILC_STATUS_OK);
2654 /** Wait for command reply */
2655 silc_fsm_next(fsm, silc_client_command_reply_wait);
2656 return SILC_FSM_CONTINUE;
2659 /********************************* SERVICE **********************************/
2661 /* Command SERVICE. Negotiates service agreement with server. */
2662 /* XXX incomplete */
2664 SILC_FSM_STATE(silc_client_command_service)
2666 SilcClientCommandContext cmd = fsm_context;
2668 SilcClientConnection conn = cmd->conn;
2672 if (cmd->argc < 2) {
2673 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2674 "Usage: /SERVICE [<service name>] [-pubkey]");
2675 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2676 return SILC_FSM_FINISH;
2679 name = cmd->argv[1];
2681 /* Send SERVICE command to the server */
2682 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2683 ++conn->cmd_ident, 1,
2684 1, name, strlen(name));
2685 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2686 NULL, 0, NULL, NULL, buffer->data,
2688 silc_buffer_free(buffer);
2691 /* Notify application */
2692 COMMAND(SILC_STATUS_OK);
2694 /** Wait for command reply */
2695 silc_fsm_next(fsm, silc_client_command_reply_wait);
2696 return SILC_FSM_CONTINUE;
2699 /* Register all default commands provided by the client library for the
2702 void silc_client_commands_register(SilcClient client)
2704 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2707 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2708 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2709 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2710 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2711 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2712 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2713 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2714 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2715 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2716 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2717 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2718 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2719 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2720 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2721 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2722 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2723 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2724 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2725 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2726 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2727 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2728 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2729 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2730 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2731 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2732 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2733 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2736 /* Unregister all commands. */
2738 void silc_client_commands_unregister(SilcClient client)
2740 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2741 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2742 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2743 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2744 SILC_CLIENT_CMDU(list, LIST, "LIST");
2745 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2746 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2747 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2748 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2749 SILC_CLIENT_CMDU(info, INFO, "INFO");
2750 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2751 SILC_CLIENT_CMDU(ping, PING, "PING");
2752 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2753 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2754 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2755 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2756 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2757 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2758 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2759 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2760 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2761 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2762 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2763 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2764 SILC_CLIENT_CMDU(users, USERS, "USERS");
2765 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2766 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2769 /****************** Client Side Incoming Command Handling *******************/
2771 /* Reply to WHOIS command from server */
2773 static void silc_client_command_process_whois(SilcClient client,
2774 SilcClientConnection conn,
2775 SilcCommandPayload payload,
2776 SilcArgumentPayload args)
2782 SilcBuffer buffer, packet;
2784 SILC_LOG_DEBUG(("Received WHOIS command"));
2786 /* Try to take the Requested Attributes */
2787 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2791 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2795 /* Process requested attributes */
2796 buffer = silc_client_attributes_process(client, conn, attrs);
2798 silc_attribute_payload_list_free(attrs);
2802 /* Send the attributes back */
2804 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2806 silc_command_get_ident(payload),
2807 1, 11, buffer->data, buffer->len);
2808 silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2809 NULL, 0, NULL, NULL, packet->data,
2811 silc_buffer_free(packet);
2812 silc_buffer_free(buffer);
2816 /* Client is able to receive some command packets even though they are
2817 special case. Server may send WHOIS command to the client to retrieve
2818 Requested Attributes information for WHOIS query the server is
2819 processing. This function currently handles only the WHOIS command,
2820 but if in the future more commands may arrive then this can be made
2821 to support other commands too. */
2823 SILC_FSM_STATE(silc_client_command)
2825 SilcClientConnection conn = fsm_context;
2826 SilcClient client = conn->client;
2827 SilcPacket packet = state_context;
2828 SilcCommandPayload payload;
2829 SilcCommand command;
2830 SilcArgumentPayload args;
2832 /* Get command payload from packet */
2833 payload = silc_command_payload_parse(packet->buffer.data,
2834 silc_buffer_len(&packet->buffer));
2836 SILC_LOG_DEBUG(("Bad command packet"));
2837 return SILC_FSM_FINISH;
2841 args = silc_command_get_args(payload);
2843 /* Get the command */
2844 command = silc_command_get(payload);
2847 case SILC_COMMAND_WHOIS:
2848 /* Ignore everything if requested by application */
2849 if (conn->internal->params.ignore_requested_attributes)
2852 silc_client_command_process_whois(client, conn, payload, args);
2859 silc_command_payload_free(payload);
2860 return SILC_FSM_FINISH;