5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 /* Command operation that is called at the end of all commands.
28 Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command( \
30 cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status) \
34 cmd->conn->client->internal->ops->command(cmd->conn->client, \
35 cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args) \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name, \
40 silc_client_command_##func, \
41 silc_client_command_reply_##func, args)
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name) \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd, \
46 silc_client_command_##func, \
47 silc_client_command_reply_##func)
49 #define SAY cmd->conn->client->internal->ops->say
51 /************************ Static utility functions **************************/
53 /* Return next available command identifier. */
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
59 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
61 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
66 /* State to finish command thread after an error in resolving command */
68 SILC_FSM_STATE(silc_client_command_continue_error)
70 /* Destructor will free all resources */
71 return SILC_FSM_FINISH;
74 /* Command reply callback to continue with the execution of a command.
75 This will continue when first successful reply is received, and ignores
76 the rest. On the other hand, if only errors are received it will
77 wait for all errors before continuing. */
79 static SilcBool silc_client_command_continue(SilcClient client,
80 SilcClientConnection conn,
87 SilcClientCommandContext cmd = context;
89 /* Continue immediately when successful reply is received */
90 if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91 SILC_FSM_CALL_CONTINUE(&cmd->thread);
98 /* Continue after last error is received */
99 if (SILC_STATUS_IS_ERROR(status) ||
100 (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102 SILC_FSM_CALL_CONTINUE(&cmd->thread);
109 /* Continues after resolving completed. */
111 static void silc_client_command_resolve_continue(SilcClient client,
112 SilcClientConnection conn,
117 SilcClientCommandContext cmd = context;
119 if (status != SILC_STATUS_OK)
120 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
122 /* Continue with the command */
123 SILC_FSM_CALL_CONTINUE(&cmd->thread);
126 /* Dummy command callback. Nothing interesting to do here. Use this when
127 you just send command but don't care about reply. */
129 SilcBool silc_client_command_called_dummy(SilcClient client,
130 SilcClientConnection conn,
140 /* Dummy resolving callback. Nothing interesting to do here. Use this
141 when you just resolve entires but don't care about reply. */
143 void silc_client_command_resolve_dummy(SilcClient client,
144 SilcClientConnection conn,
152 /* Register command to client */
155 silc_client_command_register(SilcClient client,
158 SilcFSMStateCallback command_func,
159 SilcFSMStateCallback command_reply_func,
162 SilcClientCommand cmd;
164 cmd = silc_calloc(1, sizeof(*cmd));
168 cmd->command = command_func;
169 cmd->reply = command_reply_func;
170 cmd->max_args = max_args;
171 cmd->name = name ? strdup(name) : NULL;
177 silc_list_add(client->internal->commands, cmd);
182 /* Unregister command from client */
185 silc_client_command_unregister(SilcClient client,
187 SilcFSMStateCallback command_func,
188 SilcFSMStateCallback command_reply_func)
190 SilcClientCommand cmd;
192 silc_list_start(client->internal->commands);
193 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
194 if (cmd->cmd == command && cmd->command == command_func &&
195 cmd->reply == command_reply_func) {
196 silc_list_del(client->internal->commands, cmd);
197 silc_free(cmd->name);
206 /* Finds and returns a pointer to the command list. Return NULL if the
207 command is not found. */
209 static SilcClientCommand silc_client_command_find(SilcClient client,
212 SilcClientCommand cmd;
214 silc_list_start(client->internal->commands);
215 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
216 if (cmd->name && !strcasecmp(cmd->name, name))
223 /* Command thread destructor */
225 static void silc_client_command_destructor(SilcFSMThread thread,
227 void *destructor_context)
229 SilcClientCommandContext cmd = fsm_context;
230 SilcClientConnection conn = cmd->conn;
232 /* Removes commands that aren't waiting for reply but are waiting
233 for something. They may not have been removed yet. */
234 silc_list_del(conn->internal->pending_commands, cmd);
236 silc_client_command_free(cmd);
239 /* Add a command pending a command reply. Used internally by the library. */
242 silc_client_command_add_pending(SilcClientConnection conn,
243 SilcClientCommandContext cmd,
244 SilcClientCommandReply reply,
247 SilcClientCommandReplyCallback cb;
249 silc_mutex_lock(conn->internal->lock);
251 /* Add pending callback, if defined */
253 cb = silc_calloc(1, sizeof(*cb));
255 silc_mutex_unlock(conn->internal->lock);
259 cb->context = context;
260 silc_list_add(cmd->reply_callbacks, cb);
263 /* Add pending reply */
264 silc_list_add(conn->internal->pending_commands, cmd);
266 silc_mutex_unlock(conn->internal->lock);
271 /* Generic function to send any command. The arguments must be sent already
272 encoded into correct format and in correct order. Arguments come from
273 variable argument list pointer. */
275 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
276 SilcClientConnection conn,
277 SilcClientCommandContext cmd,
279 SilcClientCommandReply reply,
281 SilcUInt32 argc, va_list ap)
285 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
287 if (conn->internal->disconnected)
291 cmd->cmd_ident = silc_client_cmd_ident(conn);
293 /* Encode command payload */
294 packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
298 /* Send the command */
299 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
300 silc_buffer_datalen(packet))) {
301 silc_buffer_free(packet);
305 /* Add the command pending command reply */
306 silc_client_command_add_pending(conn, cmd, reply, reply_context);
308 silc_buffer_free(packet);
310 return cmd->cmd_ident;
313 /* Generic function to send any command. The arguments must be sent already
314 encoded into correct format and in correct order. Arguments come from
318 silc_client_command_send_arg_array(SilcClient client,
319 SilcClientConnection conn,
320 SilcClientCommandContext cmd,
322 SilcClientCommandReply reply,
325 unsigned char **argv,
326 SilcUInt32 *argv_lens,
327 SilcUInt32 *argv_types)
331 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
333 if (conn->internal->disconnected)
337 cmd->cmd_ident = silc_client_cmd_ident(conn);
339 /* Encode command payload */
340 packet = silc_command_payload_encode(command, argc, argv, argv_lens,
341 argv_types, cmd->cmd_ident);
345 /* Send the command */
346 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
347 silc_buffer_datalen(packet))) {
348 silc_buffer_free(packet);
352 /* Add the command pending command reply */
353 silc_client_command_add_pending(conn, cmd, reply, reply_context);
355 silc_buffer_free(packet);
357 return cmd->cmd_ident;
360 /* Generic function to send any command. The arguments must be sent already
361 encoded into correct format and in correct order. This is used internally
364 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
365 SilcClientCommandContext cmd,
367 SilcClientCommandReply reply,
369 SilcUInt32 argc, ...)
372 SilcUInt16 cmd_ident;
375 cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
376 reply, reply_context, argc, ap);
382 /****************************** Command API *********************************/
384 /* Free command context and its internals */
386 void silc_client_command_free(SilcClientCommandContext cmd)
388 SilcClientCommandReplyCallback cb;
391 for (i = 0; i < cmd->argc; i++)
392 silc_free(cmd->argv[i]);
393 silc_free(cmd->argv);
394 silc_free(cmd->argv_lens);
395 silc_free(cmd->argv_types);
397 silc_list_start(cmd->reply_callbacks);
398 while ((cb = silc_list_get(cmd->reply_callbacks)))
404 /* Executes a command */
406 SilcUInt16 silc_client_command_call(SilcClient client,
407 SilcClientConnection conn,
408 const char *command_line, ...)
412 unsigned char **argv = NULL;
413 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
414 SilcClientCommand command;
415 SilcClientCommandContext cmd;
419 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
420 "You are not connected to a server, please connect to server");
424 /* Parse arguments */
425 va_start(va, command_line);
429 /* Get command name */
430 command_name = silc_memdup(command_line, strcspn(command_line, " "));
434 /* Find command by name */
435 command = silc_client_command_find(client, command_name);
437 silc_free(command_name);
441 /* Parse command line */
442 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
443 &argv_types, &argc, command->max_args);
445 silc_free(command_name);
447 arg = va_arg(va, char *);
451 /* Find command by name */
452 command = silc_client_command_find(client, arg);
457 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
458 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
459 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
460 if (!argv || !argv_lens || !argv_types)
462 argv[argc] = silc_memdup(arg, strlen(arg));
465 argv_lens[argc] = strlen(arg);
466 argv_types[argc] = argc;
468 arg = va_arg(va, char *);
473 /* Allocate command context */
474 cmd = silc_calloc(1, sizeof(*cmd));
478 cmd->cmd = command->cmd;
481 cmd->argv_lens = argv_lens;
482 cmd->argv_types = argv_types;
483 cmd->cmd_ident = silc_client_cmd_ident(conn);
486 silc_list_init(cmd->reply_callbacks,
487 struct SilcClientCommandReplyCallbackStruct, next);
490 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
491 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
492 silc_client_command_destructor, NULL, FALSE);
493 silc_fsm_start_sync(&cmd->thread, command->command);
495 return cmd->cmd_ident;
498 /* Generic function to send any command. The arguments must be sent already
499 encoded into correct format and in correct order. */
501 SilcUInt16 silc_client_command_send(SilcClient client,
502 SilcClientConnection conn,
504 SilcClientCommandReply reply,
506 SilcUInt32 argc, ...)
508 SilcClientCommandContext cmd;
514 /* Allocate command context */
515 cmd = silc_calloc(1, sizeof(*cmd));
520 silc_list_init(cmd->reply_callbacks,
521 struct SilcClientCommandReplyCallbackStruct, next);
523 /* Send the command */
526 silc_client_command_send_vap(client, conn, cmd, command, reply,
527 reply_context, argc, ap);
530 if (!cmd->cmd_ident) {
531 silc_client_command_free(cmd);
535 /*** Wait for command reply */
536 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
537 silc_client_command_destructor, NULL, FALSE);
538 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
540 return cmd->cmd_ident;
543 /* Generic function to send any command. The arguments must be sent already
544 encoded into correct format and in correct order. Arguments come from
547 SilcUInt16 silc_client_command_send_argv(SilcClient client,
548 SilcClientConnection conn,
550 SilcClientCommandReply reply,
553 unsigned char **argv,
554 SilcUInt32 *argv_lens,
555 SilcUInt32 *argv_types)
557 SilcClientCommandContext cmd;
562 /* Allocate command context */
563 cmd = silc_calloc(1, sizeof(*cmd));
569 /* Send the command */
571 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
572 reply_context, argc, argv, argv_lens,
574 if (!cmd->cmd_ident) {
575 silc_client_command_free(cmd);
579 /*** Wait for command reply */
580 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
581 silc_client_command_destructor, NULL, FALSE);
582 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
584 return cmd->cmd_ident;
587 /* Attach to a command and command identifier to receive command reply. */
589 SilcBool silc_client_command_pending(SilcClientConnection conn,
592 SilcClientCommandReply reply,
595 SilcClientCommandContext cmd;
596 SilcClientCommandReplyCallback cb;
601 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
603 silc_mutex_lock(conn->internal->lock);
605 /* Find the pending command */
606 silc_list_start(conn->internal->pending_commands);
607 while ((cmd = silc_list_get(conn->internal->pending_commands)))
608 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
609 && cmd->cmd_ident == ident) {
611 /* Add the callback */
612 cb = silc_calloc(1, sizeof(*cb));
616 cb->context = context;
617 silc_list_add(cmd->reply_callbacks, cb);
620 silc_mutex_unlock(conn->internal->lock);
625 /******************************** WHOIS *************************************/
627 /* Command WHOIS. This command is used to query information about
630 SILC_FSM_STATE(silc_client_command_whois)
632 SilcClientCommandContext cmd = fsm_context;
633 SilcClientConnection conn = cmd->conn;
634 SilcClient client = conn->client;
635 SilcBuffer attrs = NULL;
636 unsigned char count[4], *tmp = NULL;
637 SilcBool details = FALSE, nick = FALSE;
638 unsigned char *pubkey = NULL;
639 char *nickname = NULL;
642 /* Given without arguments fetches client's own information */
644 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
645 silc_buffer_data(conn->internal->local_idp),
646 silc_buffer_len(conn->internal->local_idp));
648 /* Notify application */
649 COMMAND(SILC_STATUS_OK);
651 /** Wait for command reply */
652 silc_fsm_next(fsm, silc_client_command_reply_wait);
653 return SILC_FSM_CONTINUE;
656 for (i = 1; i < cmd->argc; i++) {
657 if (!strcasecmp(cmd->argv[i], "-details")) {
659 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
660 pubkey = cmd->argv[i + 1];
663 /* We assume that the first parameter is the nickname, if it isn't
664 -details or -pubkey. The last parameter should always be the count */
667 } else if (i == cmd->argc - 1) {
668 int c = atoi(cmd->argv[i]);
669 SILC_PUT32_MSB(c, count);
676 /* If pubkey is set, add all attributes to the attrs buffer, except
679 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
680 SILC_ATTRIBUTE_SERVICE,
681 SILC_ATTRIBUTE_STATUS_MOOD,
682 SILC_ATTRIBUTE_STATUS_FREETEXT,
683 SILC_ATTRIBUTE_STATUS_MESSAGE,
684 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
685 SILC_ATTRIBUTE_PREFERRED_CONTACT,
686 SILC_ATTRIBUTE_TIMEZONE,
687 SILC_ATTRIBUTE_GEOLOCATION,
688 SILC_ATTRIBUTE_DEVICE_INFO,
689 SILC_ATTRIBUTE_USER_ICON, 0);
691 attrs = silc_client_attributes_request(0);
696 SilcAttributeObjPk obj;
699 if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
700 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
701 "Could not load public key %s, check the filename",
703 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
707 switch (silc_pkcs_get_type(pk)) {
709 obj.type = "silc-rsa";
712 obj.type = "ssh-rsa";
714 case SILC_PKCS_X509V3:
715 obj.type = "x509v3-sign-rsa";
717 case SILC_PKCS_OPENPGP:
718 obj.type = "pgp-sign-rsa";
724 obj.data = silc_pkcs_public_key_encode(NULL, pk, &obj.data_len);
726 attrs = silc_attribute_payload_encode(attrs,
727 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
728 SILC_ATTRIBUTE_FLAG_VALID,
734 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
739 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
740 3, 1, nick ? nickname : NULL,
741 nick ? strlen(nickname) : 0,
742 2, tmp ? tmp : NULL, tmp ? 4 : 0,
743 3, silc_buffer_datalen(attrs));
746 /* Notify application */
747 COMMAND(SILC_STATUS_OK);
749 /** Wait for command reply */
750 silc_fsm_next(fsm, silc_client_command_reply_wait);
751 return SILC_FSM_CONTINUE;
754 return SILC_FSM_FINISH;
757 /******************************** WHOWAS ************************************/
759 /* Command WHOWAS. This command is used to query history information about
760 specific user that used to exist in the network. */
762 SILC_FSM_STATE(silc_client_command_whowas)
764 SilcClientCommandContext cmd = fsm_context;
765 SilcClientConnection conn = cmd->conn;
766 unsigned char count[4];
769 if (cmd->argc < 2 || cmd->argc > 3) {
770 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
771 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
772 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
773 SILC_STATUS_ERR_TOO_MANY_PARAMS));
774 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 2, count, sizeof(count));
788 /* Notify application */
789 COMMAND(SILC_STATUS_OK);
791 /** Wait for command reply */
792 silc_fsm_next(fsm, silc_client_command_reply_wait);
793 return SILC_FSM_CONTINUE;
796 /******************************** IDENTIFY **********************************/
798 /* Command IDENTIFY. This command is used to query information about
799 specific user, especially ID's. */
801 SILC_FSM_STATE(silc_client_command_identify)
803 SilcClientCommandContext cmd = fsm_context;
804 SilcClientConnection conn = cmd->conn;
805 unsigned char count[4];
808 if (cmd->argc < 2 || cmd->argc > 3)
809 return SILC_FSM_FINISH;
811 if (cmd->argc == 2) {
812 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
813 1, 1, cmd->argv[1], cmd->argv_lens[1]);
815 c = atoi(cmd->argv[2]);
816 SILC_PUT32_MSB(c, count);
817 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
818 2, 1, cmd->argv[1], cmd->argv_lens[1],
819 4, count, sizeof(count));
822 /** Wait for command reply */
823 silc_fsm_next(fsm, silc_client_command_reply_wait);
824 return SILC_FSM_CONTINUE;
827 /********************************** NICK ************************************/
829 /* Command NICK. Shows current nickname/sets new nickname on current
832 SILC_FSM_STATE(silc_client_command_nick)
834 SilcClientCommandContext cmd2, cmd = fsm_context;
835 SilcClientConnection conn = cmd->conn;
838 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
839 "Usage: /NICK <nickname>");
840 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
844 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
847 /* Show current nickname */
850 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
851 "Your nickname is %s on server %s",
852 conn->local_entry->nickname, conn->remote_host);
854 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
855 "Your nickname is %s", conn->local_entry->nickname);
858 COMMAND(SILC_STATUS_OK);
862 /* If JOIN command is active, wait for it to finish before sending NICK.
863 To avoid problems locally with changing IDs while joining, we do this. */
864 silc_mutex_lock(conn->internal->lock);
865 silc_list_start(conn->internal->pending_commands);
866 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
867 if (cmd2->cmd == SILC_COMMAND_JOIN) {
868 silc_mutex_unlock(conn->internal->lock);
869 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
870 return SILC_FSM_WAIT;
873 silc_mutex_unlock(conn->internal->lock);
875 if (cmd->argv_lens[1] > 128)
876 cmd->argv_lens[1] = 128;
878 /* Send the NICK command */
879 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
880 1, 1, cmd->argv[1], cmd->argv_lens[1]);
882 /* Notify application */
883 COMMAND(SILC_STATUS_OK);
885 /** Wait for command reply */
886 silc_fsm_next(fsm, silc_client_command_reply_wait);
887 return SILC_FSM_CONTINUE;
890 return SILC_FSM_FINISH;
893 /********************************** LIST ************************************/
895 /* Command LIST. Lists channels on the current server. */
897 SILC_FSM_STATE(silc_client_command_list)
899 SilcClientCommandContext cmd = fsm_context;
900 SilcClientConnection conn = cmd->conn;
901 SilcClient client = conn->client;
902 SilcChannelEntry channel = NULL;
903 SilcBuffer idp = NULL;
905 if (cmd->argc == 2) {
906 /* Get the Channel ID of the channel */
907 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
909 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
913 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
915 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
916 1, 1, silc_buffer_datalen(idp));
918 silc_buffer_free(idp);
919 silc_client_unref_channel(client, conn, channel);
921 /* Notify application */
922 COMMAND(SILC_STATUS_OK);
924 /** Wait for command reply */
925 silc_fsm_next(fsm, silc_client_command_reply_wait);
926 return SILC_FSM_CONTINUE;
929 /********************************** TOPIC ***********************************/
931 /* Command TOPIC. Sets/shows topic on a channel. */
933 SILC_FSM_STATE(silc_client_command_topic)
935 SilcClientCommandContext cmd = fsm_context;
936 SilcClientConnection conn = cmd->conn;
937 SilcClient client = conn->client;
938 SilcChannelEntry channel;
940 char *name, tmp[512];
942 if (cmd->argc < 2 || cmd->argc > 3) {
943 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
944 "Usage: /TOPIC <channel> [<topic>]");
945 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
946 SILC_STATUS_ERR_TOO_MANY_PARAMS));
950 if (cmd->argv[1][0] == '*') {
951 if (!conn->current_channel) {
952 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
956 if (client->internal->params->full_channel_names)
957 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
959 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
960 conn->current_channel->channel_name,
961 conn->current_channel->server[0] ? "@" : "",
962 conn->current_channel->server);
968 if (!conn->current_channel) {
969 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
973 /* Get the Channel ID of the channel */
974 channel = silc_client_get_channel(conn->client, conn, name);
976 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
980 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
982 /* Send TOPIC command to the server */
984 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
985 1, silc_buffer_datalen(idp),
986 2, cmd->argv[2], strlen(cmd->argv[2]));
988 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
989 1, silc_buffer_datalen(idp));
991 silc_buffer_free(idp);
992 silc_client_unref_channel(client, conn, channel);
994 /* Notify application */
995 COMMAND(SILC_STATUS_OK);
997 /** Wait for command reply */
998 silc_fsm_next(fsm, silc_client_command_reply_wait);
999 return SILC_FSM_CONTINUE;
1002 return SILC_FSM_FINISH;
1005 /********************************* INVITE ***********************************/
1007 /* Command INVITE. Invites specific client to join a channel. This is
1008 also used to mange the invite list of the channel. */
1010 SILC_FSM_STATE(silc_client_command_invite)
1012 SilcClientCommandContext cmd = fsm_context;
1013 SilcClientConnection conn = cmd->conn;
1014 SilcClient client = conn->client;
1015 SilcClientEntry client_entry = NULL;
1016 SilcChannelEntry channel = NULL;
1017 SilcBuffer clidp, chidp, args = NULL;
1018 SilcPublicKey pubkey = NULL;
1019 SilcDList clients = NULL;
1020 char *nickname = NULL, *name;
1021 char *invite = NULL;
1022 unsigned char action[1];
1024 if (cmd->argc < 2) {
1025 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1026 "Usage: /INVITE <channel> [<nickname>[@server>]"
1027 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1028 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1032 if (cmd->argv[1][0] == '*') {
1033 if (!conn->current_channel) {
1034 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1038 channel = conn->current_channel;
1039 silc_client_ref_channel(client, conn, channel);
1041 name = cmd->argv[1];
1043 channel = silc_client_get_channel(conn->client, conn, name);
1045 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1050 /* Parse the typed nickname. */
1051 if (cmd->argc == 3) {
1052 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1053 if (!silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname)) {
1054 silc_client_unref_channel(client, conn, channel);
1058 /* Find client entry */
1059 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1062 /* Resolve client information */
1063 SILC_FSM_CALL(silc_client_get_clients(
1064 client, conn, nickname, NULL,
1065 silc_client_command_resolve_continue,
1068 client_entry = silc_dlist_get(clients);
1070 if (cmd->argv[2][0] == '+')
1075 /* Check if it is public key file to be added to invite list */
1076 silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
1077 invite = cmd->argv[2];
1084 args = silc_buffer_alloc_size(2);
1085 silc_buffer_format(args,
1086 SILC_STR_UI_SHORT(1),
1089 chidp = silc_public_key_payload_encode(NULL, pubkey);
1090 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1091 silc_buffer_len(chidp), 2);
1092 silc_buffer_free(chidp);
1093 silc_pkcs_public_key_free(pubkey);
1095 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1099 /* Send the command */
1100 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1102 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1103 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1104 1, silc_buffer_datalen(chidp),
1105 2, silc_buffer_datalen(clidp),
1106 3, args ? action : NULL, args ? 1 : 0,
1107 4, silc_buffer_datalen(args));
1108 silc_buffer_free(clidp);
1110 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1111 1, silc_buffer_datalen(chidp),
1112 3, args ? action : NULL, args ? 1 : 0,
1113 4, silc_buffer_datalen(args));
1116 silc_buffer_free(chidp);
1117 silc_buffer_free(args);
1118 silc_free(nickname);
1119 silc_client_list_free(client, conn, clients);
1120 silc_client_unref_channel(client, conn, channel);
1122 /* Notify application */
1123 COMMAND(SILC_STATUS_OK);
1125 /** Wait for command reply */
1126 silc_fsm_next(fsm, silc_client_command_reply_wait);
1127 return SILC_FSM_CONTINUE;
1130 silc_free(nickname);
1131 return SILC_FSM_FINISH;
1134 /********************************** QUIT ************************************/
1136 /* Close the connection */
1138 SILC_FSM_STATE(silc_client_command_quit_final)
1140 SilcClientCommandContext cmd = fsm_context;
1141 SilcClientConnection conn = cmd->conn;
1143 SILC_LOG_DEBUG(("Quitting"));
1145 /* Notify application */
1146 COMMAND(SILC_STATUS_OK);
1148 /* Signal to close connection */
1149 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1150 if (!conn->internal->disconnected) {
1151 conn->internal->disconnected = TRUE;
1152 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1155 return SILC_FSM_FINISH;
1158 /* Command QUIT. Closes connection with current server. */
1160 SILC_FSM_STATE(silc_client_command_quit)
1162 SilcClientCommandContext cmd = fsm_context;
1163 SilcClientConnection conn = cmd->conn;
1166 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1167 1, cmd->argv[1], cmd->argv_lens[1]);
1169 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1171 /* Sleep for a while */
1174 /* We close the connection with a little timeout */
1175 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1176 return SILC_FSM_WAIT;
1179 /********************************** KILL ************************************/
1181 /* Signature callback */
1183 static void silc_client_command_kill_signed(const SilcBuffer buffer,
1186 SilcClientCommandContext cmd = context;
1189 silc_fsm_finish(&cmd->thread);
1193 silc_fsm_set_state_context(&cmd->thread, buffer);
1194 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1197 /* Send KILL command */
1199 SILC_FSM_STATE(silc_client_command_kill_send)
1201 SilcClientCommandContext cmd = fsm_context;
1202 SilcClientConnection conn = cmd->conn;
1203 SilcClient client = conn->client;
1204 SilcBuffer idp, auth = state_context;
1205 SilcClientEntry target = cmd->context;
1206 char *comment = NULL;
1209 if (strcasecmp(cmd->argv[2], "-pubkey"))
1210 comment = cmd->argv[2];
1212 /* Send the KILL command to the server */
1213 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1214 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1215 1, silc_buffer_datalen(idp),
1216 2, comment, comment ? strlen(comment) : 0,
1217 3, silc_buffer_datalen(auth));
1219 silc_buffer_free(idp);
1220 silc_client_unref_client(client, conn, target);
1222 /* Notify application */
1223 COMMAND(SILC_STATUS_OK);
1225 /** Wait for command reply */
1226 silc_fsm_next(fsm, silc_client_command_reply_wait);
1227 return SILC_FSM_CONTINUE;
1230 /* Command KILL. Router operator can use this command to remove an client
1231 fromthe SILC Network. */
1233 SILC_FSM_STATE(silc_client_command_kill)
1235 SilcClientCommandContext cmd = fsm_context;
1236 SilcClientConnection conn = cmd->conn;
1237 SilcClient client = conn->client;
1238 SilcClientEntry target;
1240 char *nickname = NULL;
1242 if (cmd->argc < 2) {
1243 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1244 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1245 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1246 return SILC_FSM_FINISH;
1249 /* Parse the typed nickname. */
1250 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1251 return SILC_FSM_FINISH;
1253 /* Get the target client */
1254 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1256 /* Resolve client information */
1257 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1258 silc_client_command_resolve_continue,
1261 target = silc_dlist_get(clients);
1262 cmd->context = silc_client_ref_client(client, conn, target);
1264 silc_free(nickname);
1265 silc_client_list_free(client, conn, clients);
1268 silc_fsm_next(fsm, silc_client_command_kill_send);
1270 if (cmd->argc >= 3) {
1271 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1272 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1273 /* Encode the public key authentication payload */
1274 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1278 conn->internal->sha1hash,
1279 &target->id, SILC_ID_CLIENT,
1280 silc_client_command_kill_signed,
1286 return SILC_FSM_CONTINUE;
1289 /********************************** INFO ************************************/
1291 /* Command INFO. Request information about specific server. If specific
1292 server is not provided the current server is used. */
1294 SILC_FSM_STATE(silc_client_command_info)
1296 SilcClientCommandContext cmd = fsm_context;
1297 SilcClientConnection conn = cmd->conn;
1299 /* Send the command */
1301 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1302 1, cmd->argv[1], cmd->argv_lens[1]);
1304 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1306 /* Notify application */
1307 COMMAND(SILC_STATUS_OK);
1309 /** Wait for command reply */
1310 silc_fsm_next(fsm, silc_client_command_reply_wait);
1311 return SILC_FSM_CONTINUE;
1314 /********************************** STATS ***********************************/
1316 /* Command STATS. Shows server and network statistics. */
1318 SILC_FSM_STATE(silc_client_command_stats)
1320 SilcClientCommandContext cmd = fsm_context;
1321 SilcClientConnection conn = cmd->conn;
1323 /* Send the command */
1324 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1325 1, silc_buffer_datalen(conn->internal->
1328 /* Notify application */
1329 COMMAND(SILC_STATUS_OK);
1331 /** Wait for command reply */
1332 silc_fsm_next(fsm, silc_client_command_reply_wait);
1333 return SILC_FSM_CONTINUE;
1336 /********************************** PING ************************************/
1338 /* Command PING. Sends ping to server. */
1340 SILC_FSM_STATE(silc_client_command_ping)
1342 SilcClientCommandContext cmd = fsm_context;
1343 SilcClientConnection conn = cmd->conn;
1345 if (cmd->argc < 2) {
1346 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1347 return SILC_FSM_FINISH;
1350 /* Send the command */
1351 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1352 1, silc_buffer_datalen(conn->internal->
1355 /* Save ping time */
1356 cmd->context = SILC_64_TO_PTR(silc_time());
1358 /* Notify application */
1359 COMMAND(SILC_STATUS_OK);
1361 /** Wait for command reply */
1362 silc_fsm_next(fsm, silc_client_command_reply_wait);
1363 return SILC_FSM_CONTINUE;
1366 /********************************** JOIN ************************************/
1372 } *SilcClientJoinContext;
1374 /* Signature callback */
1376 static void silc_client_command_join_signed(const SilcBuffer buffer,
1379 SilcClientCommandContext cmd = context;
1380 SilcClientJoinContext j = cmd->context;
1383 silc_fsm_finish(&cmd->thread);
1388 j->auth = silc_buffer_copy(buffer);
1390 j->cauth = silc_buffer_copy(buffer);
1392 SILC_FSM_CALL_CONTINUE(&cmd->thread);
1395 /* Command JOIN. Joins to a channel. */
1397 SILC_FSM_STATE(silc_client_command_join)
1399 SilcClientCommandContext cmd2, cmd = fsm_context;
1400 SilcClientConnection conn = cmd->conn;
1401 SilcClient client = conn->client;
1402 SilcChannelEntry channel = NULL;
1403 SilcClientJoinContext j = cmd->context;
1404 SilcBuffer auth = NULL, cauth = NULL;
1405 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1406 int i, passphrase_len = 0;
1408 if (cmd->argc < 2) {
1409 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1413 /* See if we have joined to the requested channel already */
1414 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1415 if (channel && silc_client_on_channel(channel, conn->local_entry))
1418 /* If NICK command is active, wait for it to finish before sending JOIN.
1419 To avoid problems locally with changing IDs while joining, we do this. */
1420 silc_mutex_lock(conn->internal->lock);
1421 silc_list_start(conn->internal->pending_commands);
1422 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1423 if (cmd2->cmd == SILC_COMMAND_NICK) {
1424 silc_mutex_unlock(conn->internal->lock);
1425 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1426 return SILC_FSM_WAIT;
1429 silc_mutex_unlock(conn->internal->lock);
1431 if (cmd->argv_lens[1] > 256)
1432 cmd->argv_lens[1] = 256;
1434 name = cmd->argv[1];
1436 for (i = 2; i < cmd->argc; i++) {
1437 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1438 cipher = cmd->argv[++i];
1439 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1440 hmac = cmd->argv[++i];
1441 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1442 if (!j || !j->auth) {
1444 j = silc_calloc(1, sizeof(*j));
1450 silc_free(passphrase);
1451 silc_client_unref_channel(client, conn, channel);
1452 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1456 conn->internal->sha1hash,
1459 silc_client_command_join_signed,
1463 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1464 SilcPublicKey pubkey = conn->public_key;
1465 SilcPrivateKey privkey = conn->private_key;
1466 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], pubdata[128];
1469 if (!j || !j->cauth) {
1471 j = silc_calloc(1, sizeof(*j));
1477 if (cmd->argc >= i + 3) {
1479 if (cmd->argc >= i + 4) {
1480 pass = cmd->argv[i + 3];
1483 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1484 &pubkey, &privkey)) {
1485 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1486 "Could not load key pair, check your arguments");
1487 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1494 pk = silc_pkcs_public_key_encode(NULL, pubkey, &pk_len);
1495 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1497 silc_rng_get_rn_data(conn->client->rng, sizeof(pubdata), pubdata,
1499 memcpy(pubdata, pkhash, 20);
1500 silc_free(passphrase);
1501 silc_client_unref_channel(client, conn, channel);
1502 SILC_FSM_CALL(silc_auth_public_key_auth_generate_wpub(
1504 pubdata, sizeof(pubdata),
1505 conn->internal->sha1hash,
1509 silc_client_command_join_signed,
1514 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1515 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1516 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1517 cmd->argv_lens[i], 0);
1518 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1519 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1520 0, pu8, passphrase_len);
1523 passphrase = strdup(cmd->argv[i]);
1524 passphrase_len = cmd->argv_lens[i];
1529 /* Send JOIN command to the server */
1530 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1531 1, name, strlen(name),
1532 2, silc_buffer_datalen(conn->internal->
1534 3, passphrase, passphrase_len,
1535 4, cipher, cipher ? strlen(cipher) : 0,
1536 5, hmac, hmac ? strlen(hmac) : 0,
1537 6, silc_buffer_datalen(auth),
1538 7, silc_buffer_datalen(cauth));
1541 memset(passphrase, 0, strlen(passphrase));
1542 silc_free(passphrase);
1543 silc_client_unref_channel(client, conn, channel);
1545 silc_buffer_free(j->auth);
1546 silc_buffer_free(j->cauth);
1550 /* Notify application */
1551 COMMAND(SILC_STATUS_OK);
1553 /** Wait for command reply */
1554 silc_fsm_next(fsm, silc_client_command_reply_wait);
1555 return SILC_FSM_CONTINUE;
1558 silc_client_unref_channel(client, conn, channel);
1559 return SILC_FSM_FINISH;
1562 /********************************** MOTD ************************************/
1564 /* MOTD command. Requests motd from server. */
1566 SILC_FSM_STATE(silc_client_command_motd)
1568 SilcClientCommandContext cmd = fsm_context;
1569 SilcClientConnection conn = cmd->conn;
1571 if (cmd->argc < 1 || cmd->argc > 2) {
1572 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1573 "Usage: /MOTD [<server>]");
1574 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1575 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1576 return SILC_FSM_FINISH;
1579 /* Send the command */
1581 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1582 1, conn->remote_host,
1583 strlen(conn->remote_host));
1585 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1586 1, cmd->argv[1], cmd->argv_lens[1]);
1588 /* Notify application */
1589 COMMAND(SILC_STATUS_OK);
1591 /** Wait for command reply */
1592 silc_fsm_next(fsm, silc_client_command_reply_wait);
1593 return SILC_FSM_CONTINUE;
1596 /********************************** UMODE ***********************************/
1598 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1599 modes as client cannot set itself server/router operator privileges. */
1601 SILC_FSM_STATE(silc_client_command_umode)
1603 SilcClientCommandContext cmd = fsm_context;
1604 SilcClientConnection conn = cmd->conn;
1605 unsigned char *cp, modebuf[4];
1606 SilcUInt32 mode, add, len;
1609 if (cmd->argc < 2) {
1610 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1611 "Usage: /UMODE +|-<modes>");
1612 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1613 return SILC_FSM_FINISH;
1616 mode = conn->local_entry->mode;
1618 /* Are we adding or removing mode */
1619 if (cmd->argv[1][0] == '-')
1625 cp = cmd->argv[1] + 1;
1627 for (i = 0; i < len; i++) {
1632 mode |= SILC_UMODE_SERVER_OPERATOR;
1633 mode |= SILC_UMODE_ROUTER_OPERATOR;
1634 mode |= SILC_UMODE_GONE;
1635 mode |= SILC_UMODE_INDISPOSED;
1636 mode |= SILC_UMODE_BUSY;
1637 mode |= SILC_UMODE_PAGE;
1638 mode |= SILC_UMODE_HYPER;
1639 mode |= SILC_UMODE_ROBOT;
1640 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1641 mode |= SILC_UMODE_REJECT_WATCHING;
1643 mode = SILC_UMODE_NONE;
1648 mode |= SILC_UMODE_SERVER_OPERATOR;
1650 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1654 mode |= SILC_UMODE_ROUTER_OPERATOR;
1656 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1660 mode |= SILC_UMODE_GONE;
1662 mode &= ~SILC_UMODE_GONE;
1666 mode |= SILC_UMODE_INDISPOSED;
1668 mode &= ~SILC_UMODE_INDISPOSED;
1672 mode |= SILC_UMODE_BUSY;
1674 mode &= ~SILC_UMODE_BUSY;
1678 mode |= SILC_UMODE_PAGE;
1680 mode &= ~SILC_UMODE_PAGE;
1684 mode |= SILC_UMODE_HYPER;
1686 mode &= ~SILC_UMODE_HYPER;
1690 mode |= SILC_UMODE_ROBOT;
1692 mode &= ~SILC_UMODE_ROBOT;
1696 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1698 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1702 mode |= SILC_UMODE_REJECT_WATCHING;
1704 mode &= ~SILC_UMODE_REJECT_WATCHING;
1708 mode |= SILC_UMODE_BLOCK_INVITE;
1710 mode &= ~SILC_UMODE_BLOCK_INVITE;
1713 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1714 return SILC_FSM_FINISH;
1719 SILC_PUT32_MSB(mode, modebuf);
1721 /* Send the command */
1722 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1723 1, silc_buffer_datalen(conn->internal->
1725 2, modebuf, sizeof(modebuf));
1727 /* Notify application */
1728 COMMAND(SILC_STATUS_OK);
1730 /** Wait for command reply */
1731 silc_fsm_next(fsm, silc_client_command_reply_wait);
1732 return SILC_FSM_CONTINUE;
1735 /********************************** CMODE ***********************************/
1737 /* Signature callback */
1739 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1742 SilcClientCommandContext cmd = context;
1745 silc_fsm_finish(&cmd->thread);
1749 silc_fsm_set_state_context(&cmd->thread, buffer);
1750 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1753 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1754 can be set several at once. Those modes that require argument must be set
1755 separately (unless set with modes that does not require arguments). */
1757 SILC_FSM_STATE(silc_client_command_cmode)
1759 SilcClientCommandContext cmd = fsm_context;
1760 SilcClientConnection conn = cmd->conn;
1761 SilcClient client = conn->client;
1762 SilcBuffer auth = state_context;
1763 SilcChannelEntry channel = NULL;
1764 SilcBuffer chidp, pk = NULL;
1765 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1766 SilcUInt32 mode, add, type, len, arg_len = 0;
1769 if (cmd->argc < 3) {
1770 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1771 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1772 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1776 if (cmd->argv[1][0] == '*') {
1777 if (!conn->current_channel) {
1778 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1782 channel = conn->current_channel;
1783 silc_client_ref_channel(client, conn, channel);
1785 name = cmd->argv[1];
1787 channel = silc_client_get_channel(conn->client, conn, name);
1789 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1794 mode = channel->mode;
1796 /* Are we adding or removing mode */
1797 if (cmd->argv[2][0] == '-')
1802 /* Argument type to be sent to server */
1806 cp = cmd->argv[2] + 1;
1808 for (i = 0; i < len; i++) {
1812 mode |= SILC_CHANNEL_MODE_PRIVATE;
1814 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1818 mode |= SILC_CHANNEL_MODE_SECRET;
1820 mode &= ~SILC_CHANNEL_MODE_SECRET;
1824 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1826 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1830 mode |= SILC_CHANNEL_MODE_INVITE;
1832 mode &= ~SILC_CHANNEL_MODE_INVITE;
1836 mode |= SILC_CHANNEL_MODE_TOPIC;
1838 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1842 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1844 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1848 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1850 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1855 mode |= SILC_CHANNEL_MODE_ULIMIT;
1857 if (cmd->argc < 4) {
1858 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1859 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1860 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1863 ll = atoi(cmd->argv[3]);
1864 SILC_PUT32_MSB(ll, tmp);
1868 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1873 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1875 if (cmd->argc < 4) {
1876 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1877 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1878 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1882 arg_len = cmd->argv_lens[3];
1884 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1889 mode |= SILC_CHANNEL_MODE_CIPHER;
1891 if (cmd->argc < 4) {
1892 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1893 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1894 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1898 arg_len = cmd->argv_lens[3];
1900 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1905 mode |= SILC_CHANNEL_MODE_HMAC;
1907 if (cmd->argc < 4) {
1908 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1909 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1910 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1914 arg_len = cmd->argv_lens[3];
1916 mode &= ~SILC_CHANNEL_MODE_HMAC;
1922 SilcPublicKey pubkey = conn->public_key;
1923 SilcPrivateKey privkey = conn->private_key;
1925 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1928 if (cmd->argc >= 5) {
1931 pass = cmd->argv[5];
1932 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1933 &pubkey, &privkey)) {
1934 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1935 "Could not load key pair, check your arguments");
1936 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1941 pk = silc_public_key_payload_encode(NULL, pubkey);
1942 silc_client_unref_channel(client, conn, channel);
1943 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1946 conn->internal->sha1hash,
1949 silc_client_command_cmode_signed,
1953 arg = silc_buffer_data(auth);
1954 arg_len = silc_buffer_len(auth);
1956 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1962 SilcBool chadd = FALSE;
1963 SilcPublicKey chpk = NULL;
1965 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1968 if (cmd->argc == 3) {
1969 /* Send empty command to receive the public key list. */
1970 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1971 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1973 1, silc_buffer_datalen(chidp));
1974 silc_buffer_free(chidp);
1975 silc_client_unref_channel(client, conn, channel);
1977 /* Notify application */
1978 COMMAND(SILC_STATUS_OK);
1980 /** Wait for command reply */
1981 silc_fsm_next(fsm, silc_client_command_reply_wait);
1982 return SILC_FSM_CONTINUE;
1985 if (cmd->argc >= 4) {
1986 auth = silc_buffer_alloc_size(2);
1987 silc_buffer_format(auth,
1988 SILC_STR_UI_SHORT(cmd->argc - 3),
1992 for (k = 3; k < cmd->argc; k++) {
1993 if (cmd->argv[k][0] == '+')
1995 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, SILC_PKCS_ANY,
1997 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1998 "Could not load public key %s, check the filename",
2000 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2001 silc_buffer_free(auth);
2006 pk = silc_public_key_payload_encode(NULL, chpk);
2007 auth = silc_argument_payload_encode_one(auth,
2008 silc_buffer_datalen(pk),
2009 chadd ? 0x00 : 0x01);
2010 silc_pkcs_public_key_free(chpk);
2011 silc_buffer_free(pk);
2016 arg = silc_buffer_data(auth);
2017 arg_len = silc_buffer_len(auth);
2019 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2023 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2029 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2030 SILC_PUT32_MSB(mode, modebuf);
2032 /* Send the command. We support sending only one mode at once that
2033 requires an argument. */
2035 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2036 1, silc_buffer_datalen(chidp),
2037 2, modebuf, sizeof(modebuf),
2039 8, silc_buffer_datalen(pk));
2041 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2042 1, silc_buffer_datalen(chidp),
2043 2, modebuf, sizeof(modebuf));
2046 silc_buffer_free(chidp);
2047 silc_buffer_free(auth);
2048 silc_buffer_free(pk);
2049 silc_client_unref_channel(client, conn, channel);
2051 /* Notify application */
2052 COMMAND(SILC_STATUS_OK);
2054 /** Wait for command reply */
2055 silc_fsm_next(fsm, silc_client_command_reply_wait);
2056 return SILC_FSM_CONTINUE;
2059 silc_client_unref_channel(client, conn, channel);
2060 return SILC_FSM_FINISH;
2063 /********************************* CUMODE ***********************************/
2065 /* Signature callback */
2067 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2070 SilcClientCommandContext cmd = context;
2073 silc_fsm_finish(&cmd->thread);
2077 silc_fsm_set_state_context(&cmd->thread, buffer);
2078 SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2081 /* CUMODE command. Changes client's mode on a channel. */
2083 SILC_FSM_STATE(silc_client_command_cumode)
2085 SilcClientCommandContext cmd = fsm_context;
2086 SilcClientConnection conn = cmd->conn;
2087 SilcClient client = conn->client;
2088 SilcBuffer auth = state_context;
2089 SilcChannelEntry channel = NULL;
2090 SilcChannelUser chu;
2091 SilcClientEntry client_entry;
2092 SilcBuffer clidp, chidp;
2093 SilcDList clients = NULL;
2094 unsigned char *name, *cp, modebuf[4];
2095 SilcUInt32 mode = 0, add, len;
2096 char *nickname = NULL;
2099 if (cmd->argc < 4) {
2100 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2101 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2102 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2106 if (cmd->argv[1][0] == '*') {
2107 if (!conn->current_channel) {
2108 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2112 channel = conn->current_channel;
2113 silc_client_ref_channel(client, conn, channel);
2115 name = cmd->argv[1];
2117 channel = silc_client_get_channel(conn->client, conn, name);
2119 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2124 /* Parse the typed nickname. */
2125 if (!silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname))
2128 /* Find client entry */
2129 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2131 /* Resolve client information */
2132 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2133 silc_client_command_resolve_continue,
2136 client_entry = silc_dlist_get(clients);
2138 /* Get the current mode */
2139 chu = silc_client_on_channel(channel, client_entry);
2143 /* Are we adding or removing mode */
2144 if (cmd->argv[2][0] == '-')
2150 cp = cmd->argv[2] + 1;
2152 for (i = 0; i < len; i++) {
2156 mode |= SILC_CHANNEL_UMODE_CHANFO;
2157 mode |= SILC_CHANNEL_UMODE_CHANOP;
2158 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2159 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2160 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2162 mode = SILC_CHANNEL_UMODE_NONE;
2168 SilcPublicKey pubkey = conn->public_key;
2169 SilcPrivateKey privkey = conn->private_key;
2171 if (cmd->argc >= 6) {
2174 pass = cmd->argv[6];
2175 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2176 &pubkey, &privkey)) {
2177 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2178 "Could not load key pair, check your arguments");
2179 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2184 silc_free(nickname);
2185 silc_client_list_free(client, conn, clients);
2186 silc_client_unref_channel(client, conn, channel);
2188 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2191 conn->internal->sha1hash,
2194 silc_client_command_cumode_signed,
2199 mode |= SILC_CHANNEL_UMODE_CHANFO;
2201 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2206 mode |= SILC_CHANNEL_UMODE_CHANOP;
2208 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2212 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2214 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2218 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2220 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2224 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2226 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2230 mode |= SILC_CHANNEL_UMODE_QUIET;
2232 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2235 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2241 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2242 SILC_PUT32_MSB(mode, modebuf);
2243 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2245 /* Send the command packet. We support sending only one mode at once
2246 that requires an argument. */
2247 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2248 1, silc_buffer_datalen(chidp),
2250 3, silc_buffer_datalen(clidp),
2251 4, silc_buffer_datalen(auth));
2253 silc_buffer_free(chidp);
2254 silc_buffer_free(clidp);
2256 silc_buffer_free(auth);
2257 silc_free(nickname);
2258 silc_client_list_free(client, conn, clients);
2259 silc_client_unref_channel(client, conn, channel);
2261 /* Notify application */
2262 COMMAND(SILC_STATUS_OK);
2264 /** Wait for command reply */
2265 silc_fsm_next(fsm, silc_client_command_reply_wait);
2266 return SILC_FSM_CONTINUE;
2269 silc_client_unref_channel(client, conn, channel);
2270 silc_client_list_free(client, conn, clients);
2271 silc_free(nickname);
2272 return SILC_FSM_FINISH;
2275 /********************************** KICK ************************************/
2277 /* KICK command. Kicks a client out of channel. */
2279 SILC_FSM_STATE(silc_client_command_kick)
2281 SilcClientCommandContext cmd = fsm_context;
2282 SilcClientConnection conn = cmd->conn;
2283 SilcClient client = conn->client;
2284 SilcChannelEntry channel = NULL;
2285 SilcBuffer idp, idp2;
2286 SilcClientEntry target;
2287 SilcDList clients = NULL;
2288 char *name, tmp[512];
2290 if (cmd->argc < 3) {
2291 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2292 "Usage: /KICK <channel> <nickname> [<comment>]");
2293 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2297 if (cmd->argv[1][0] == '*') {
2298 if (!conn->current_channel) {
2299 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2303 if (client->internal->params->full_channel_names)
2304 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2306 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2307 conn->current_channel->channel_name,
2308 conn->current_channel->server[0] ? "@" : "",
2309 conn->current_channel->server);
2312 name = cmd->argv[1];
2315 if (!conn->current_channel) {
2316 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2320 /* Get the Channel ID of the channel */
2321 channel = silc_client_get_channel(conn->client, conn, name);
2323 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2327 /* Get the target client */
2328 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2330 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2331 "No such client: %s", cmd->argv[2]);
2332 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2335 target = silc_dlist_get(clients);
2337 /* Send KICK command to the server */
2338 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2339 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2341 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2342 1, silc_buffer_datalen(idp),
2343 2, silc_buffer_datalen(idp2));
2345 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2346 1, silc_buffer_datalen(idp),
2347 2, silc_buffer_datalen(idp2),
2348 3, cmd->argv[3], strlen(cmd->argv[3]));
2350 silc_buffer_free(idp);
2351 silc_buffer_free(idp2);
2352 silc_client_list_free(client, conn, clients);
2353 silc_client_unref_channel(client, conn, channel);
2355 /* Notify application */
2356 COMMAND(SILC_STATUS_OK);
2358 /** Wait for command reply */
2359 silc_fsm_next(fsm, silc_client_command_reply_wait);
2360 return SILC_FSM_CONTINUE;
2363 silc_client_unref_channel(client, conn, channel);
2364 return SILC_FSM_FINISH;
2367 /***************************** OPER & SILCOPER ******************************/
2370 unsigned char *passphrase;
2371 SilcUInt32 passphrase_len;
2373 } *SilcClientCommandOper;
2375 /* Ask passphrase callback */
2377 static void silc_client_command_oper_cb(const unsigned char *data,
2378 SilcUInt32 data_len, void *context)
2380 SilcClientCommandContext cmd = context;
2381 SilcClientCommandOper oper = cmd->context;
2383 if (data && data_len)
2384 oper->passphrase = silc_memdup(data, data_len);
2385 oper->passphrase_len = data_len;
2388 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2391 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2394 SilcClientCommandContext cmd = context;
2395 SilcClientCommandOper oper = cmd->context;
2398 oper->auth = silc_buffer_copy(data);
2401 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2404 /* Send OPER/SILCOPER command */
2406 SILC_FSM_STATE(silc_client_command_oper_send)
2408 SilcClientCommandContext cmd = fsm_context;
2409 SilcClientConnection conn = cmd->conn;
2410 SilcClientCommandOper oper = cmd->context;
2411 SilcBuffer auth = oper ? oper->auth : NULL;
2413 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2414 1, cmd->argv[1], strlen(cmd->argv[1]),
2415 2, silc_buffer_datalen(auth));
2417 silc_buffer_clear(auth);
2418 silc_buffer_free(auth);
2420 silc_free(oper->passphrase);
2424 /* Notify application */
2425 COMMAND(SILC_STATUS_OK);
2427 /** Wait for command reply */
2428 silc_fsm_next(fsm, silc_client_command_reply_wait);
2429 return SILC_FSM_CONTINUE;
2432 /* Get authentication data */
2434 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2436 SilcClientCommandContext cmd = fsm_context;
2437 SilcClientConnection conn = cmd->conn;
2438 SilcClientCommandOper oper = cmd->context;
2440 silc_fsm_next(fsm, silc_client_command_oper_send);
2442 if (!oper || !oper->passphrase) {
2443 /* Encode the public key authentication payload */
2444 SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2448 conn->internal->hash,
2449 conn->local_id, SILC_ID_CLIENT,
2450 silc_client_command_oper_sign_cb,
2455 /* Encode the password authentication payload */
2456 oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2457 oper->passphrase, oper->passphrase_len);
2459 return SILC_FSM_CONTINUE;
2462 /* OPER command. Used to obtain server operator privileges. */
2464 SILC_FSM_STATE(silc_client_command_oper)
2466 SilcClientCommandContext cmd = fsm_context;
2467 SilcClientConnection conn = cmd->conn;
2468 SilcClientCommandOper oper;
2470 if (cmd->argc < 2) {
2471 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2472 "Usage: /OPER <username> [-pubkey]");
2473 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2474 return SILC_FSM_FINISH;
2477 silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2479 /* Get passphrase */
2480 if (cmd->argc < 3) {
2481 oper = silc_calloc(1, sizeof(*oper));
2483 return SILC_FSM_FINISH;
2484 cmd->context = oper;
2485 SILC_FSM_CALL(conn->client->internal->
2486 ops->ask_passphrase(conn->client, conn,
2487 silc_client_command_oper_cb, cmd));
2490 return SILC_FSM_CONTINUE;
2493 /* SILCOPER command. Used to obtain router operator privileges. */
2495 SILC_FSM_STATE(silc_client_command_silcoper)
2497 SilcClientCommandContext cmd = fsm_context;
2498 SilcClientConnection conn = cmd->conn;
2499 SilcClientCommandOper oper;
2501 if (cmd->argc < 2) {
2502 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2503 "Usage: /SILCOPER <username> [-pubkey]");
2504 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2505 return SILC_FSM_FINISH;
2508 silc_fsm_next(fsm, silc_client_command_oper_send);
2510 /* Get passphrase */
2511 if (cmd->argc < 3) {
2512 oper = silc_calloc(1, sizeof(*oper));
2514 return SILC_FSM_FINISH;
2515 cmd->context = oper;
2516 SILC_FSM_CALL(conn->client->internal->
2517 ops->ask_passphrase(conn->client, conn,
2518 silc_client_command_oper_cb, cmd));
2521 return SILC_FSM_CONTINUE;
2524 /*********************************** BAN ************************************/
2526 /* Command BAN. This is used to manage the ban list of the channel. */
2528 SILC_FSM_STATE(silc_client_command_ban)
2530 SilcClientCommandContext cmd = fsm_context;
2531 SilcClientConnection conn = cmd->conn;
2532 SilcClient client = conn->client;
2533 SilcChannelEntry channel;
2534 SilcBuffer chidp, args = NULL;
2535 char *name, *ban = NULL;
2536 unsigned char action[1];
2537 SilcPublicKey pubkey = NULL;
2539 if (cmd->argc < 2) {
2540 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2541 "Usage: /BAN <channel> "
2542 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2543 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2547 if (cmd->argv[1][0] == '*') {
2548 if (!conn->current_channel) {
2549 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2553 channel = conn->current_channel;
2554 silc_client_ref_channel(client, conn, channel);
2556 name = cmd->argv[1];
2558 channel = silc_client_get_channel(conn->client, conn, name);
2560 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2565 if (cmd->argc == 3) {
2566 if (cmd->argv[2][0] == '+')
2571 /* Check if it is public key file to be added to invite list */
2572 silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
2579 args = silc_buffer_alloc_size(2);
2580 silc_buffer_format(args,
2581 SILC_STR_UI_SHORT(1),
2584 chidp = silc_public_key_payload_encode(NULL, pubkey);
2585 args = silc_argument_payload_encode_one(args,
2586 silc_buffer_datalen(chidp), 2);
2587 silc_buffer_free(chidp);
2588 silc_pkcs_public_key_free(pubkey);
2590 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2594 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2596 /* Send the command */
2597 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2598 1, silc_buffer_datalen(chidp),
2599 2, args ? action : NULL, args ? 1 : 0,
2600 3, silc_buffer_datalen(args));
2602 silc_buffer_free(chidp);
2603 silc_buffer_free(args);
2604 silc_client_unref_channel(client, conn, channel);
2606 /* Notify application */
2607 COMMAND(SILC_STATUS_OK);
2609 /** Wait for command reply */
2610 silc_fsm_next(fsm, silc_client_command_reply_wait);
2611 return SILC_FSM_CONTINUE;
2614 return SILC_FSM_FINISH;
2617 /********************************* DETACH ***********************************/
2619 /* Command DETACH. This is used to detach from the server */
2621 SILC_FSM_STATE(silc_client_command_detach)
2623 SilcClientCommandContext cmd = fsm_context;
2624 SilcClientConnection conn = cmd->conn;
2626 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2628 /* Notify application */
2629 COMMAND(SILC_STATUS_OK);
2631 /** Wait for command reply */
2632 silc_fsm_next(fsm, silc_client_command_reply_wait);
2633 return SILC_FSM_CONTINUE;
2636 /********************************** WATCH ***********************************/
2638 /* Command WATCH. */
2640 SILC_FSM_STATE(silc_client_command_watch)
2642 SilcClientCommandContext cmd = fsm_context;
2643 SilcClientConnection conn = cmd->conn;
2644 SilcBuffer args = NULL;
2646 const char *pubkey = NULL;
2647 SilcBool pubkey_add = TRUE;
2649 if (cmd->argc < 3) {
2650 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2654 if (!strcasecmp(cmd->argv[1], "-add")) {
2656 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2658 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2660 pubkey = cmd->argv[2] + 1;
2661 if (cmd->argv[2][0] == '-')
2664 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2672 if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
2673 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2674 "Could not load public key %s, check the filename", pubkey);
2675 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2679 args = silc_buffer_alloc_size(2);
2680 silc_buffer_format(args,
2681 SILC_STR_UI_SHORT(1),
2683 buffer = silc_public_key_payload_encode(NULL, pk);
2684 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2685 pubkey_add ? 0x00 : 0x01);
2686 silc_buffer_free(buffer);
2687 silc_pkcs_public_key_free(pk);
2690 /* If watching by nickname, resolve all users with that nickname so that
2691 we get their information immediately. */
2693 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2694 silc_client_command_resolve_dummy, NULL);
2696 /* Send the commmand */
2697 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2698 1, silc_buffer_datalen(conn->internal->
2700 type, pubkey ? args->data : cmd->argv[2],
2701 pubkey ? silc_buffer_len(args) :
2704 silc_buffer_free(args);
2706 /* Notify application */
2707 COMMAND(SILC_STATUS_OK);
2709 /** Wait for command reply */
2710 silc_fsm_next(fsm, silc_client_command_reply_wait);
2711 return SILC_FSM_CONTINUE;
2714 return SILC_FSM_FINISH;
2717 /********************************** LEAVE ***********************************/
2719 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2721 SILC_FSM_STATE(silc_client_command_leave)
2723 SilcClientCommandContext cmd = fsm_context;
2724 SilcClientConnection conn = cmd->conn;
2725 SilcClient client = conn->client;
2726 SilcChannelEntry channel;
2728 char *name, tmp[512];
2730 if (cmd->argc != 2) {
2731 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2732 "Usage: /LEAVE <channel>");
2733 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2737 if (cmd->argv[1][0] == '*') {
2738 if (!conn->current_channel) {
2739 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2743 if (client->internal->params->full_channel_names)
2744 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2746 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2747 conn->current_channel->channel_name,
2748 conn->current_channel->server[0] ? "@" : "",
2749 conn->current_channel->server);
2752 name = cmd->argv[1];
2755 /* Get the channel entry */
2756 channel = silc_client_get_channel(conn->client, conn, name);
2758 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2762 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2764 /* Send LEAVE command to the server */
2765 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2766 1, silc_buffer_datalen(idp));
2768 silc_buffer_free(idp);
2770 /* Notify application */
2771 COMMAND(SILC_STATUS_OK);
2773 if (conn->current_channel == channel)
2774 conn->current_channel = NULL;
2776 silc_client_unref_channel(client, conn, channel);
2778 /** Wait for command reply */
2779 silc_fsm_next(fsm, silc_client_command_reply_wait);
2780 return SILC_FSM_CONTINUE;
2783 return SILC_FSM_FINISH;
2786 /********************************** USERS ***********************************/
2788 /* Command USERS. Requests the USERS of the clients joined on requested
2791 SILC_FSM_STATE(silc_client_command_users)
2793 SilcClientCommandContext cmd = fsm_context;
2794 SilcClientConnection conn = cmd->conn;
2795 char *name, tmp[512];
2797 if (cmd->argc != 2) {
2798 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2799 "Usage: /USERS <channel>");
2800 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2804 if (cmd->argv[1][0] == '*') {
2805 if (!conn->current_channel) {
2806 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2810 if (conn->client->internal->params->full_channel_names)
2811 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2813 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2814 conn->current_channel->channel_name,
2815 conn->current_channel->server[0] ? "@" : "",
2816 conn->current_channel->server);
2819 name = cmd->argv[1];
2822 /* Send USERS command to the server */
2823 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2824 2, name, strlen(name));
2826 /* Notify application */
2827 COMMAND(SILC_STATUS_OK);
2829 /** Wait for command reply */
2830 silc_fsm_next(fsm, silc_client_command_reply_wait);
2831 return SILC_FSM_CONTINUE;
2834 return SILC_FSM_FINISH;
2837 /********************************* GETKEY ***********************************/
2839 /* Command GETKEY. Used to fetch remote client's public key. */
2841 SILC_FSM_STATE(silc_client_command_getkey)
2843 SilcClientCommandContext cmd = fsm_context;
2844 SilcClientConnection conn = cmd->conn;
2845 SilcClient client = conn->client;
2846 SilcClientEntry client_entry;
2847 SilcServerEntry server_entry;
2851 if (cmd->argc < 2) {
2852 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2853 "Usage: /GETKEY <nickname or server name>");
2854 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2855 return SILC_FSM_FINISH;
2858 /* Find client entry */
2859 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2861 /* Check whether user requested server */
2862 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2863 if (!server_entry) {
2864 if (cmd->resolved) {
2865 /* Resolving didn't find anything. We should never get here as
2866 errors are handled in the resolving callback. */
2867 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2868 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2869 return SILC_FSM_FINISH;
2872 /* No client or server exist with this name, query for both. */
2873 cmd->resolved = TRUE;
2874 SILC_FSM_CALL(silc_client_command_send(client, conn,
2875 SILC_COMMAND_IDENTIFY,
2876 silc_client_command_continue,
2879 strlen(cmd->argv[1]),
2881 strlen(cmd->argv[1])));
2884 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2885 silc_client_unref_server(client, conn, server_entry);
2887 client_entry = silc_dlist_get(clients);
2888 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2889 silc_client_list_free(client, conn, clients);
2892 /* Send the commmand */
2893 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2894 1, silc_buffer_datalen(idp));
2896 silc_buffer_free(idp);
2898 /* Notify application */
2899 COMMAND(SILC_STATUS_OK);
2901 /** Wait for command reply */
2902 silc_fsm_next(fsm, silc_client_command_reply_wait);
2903 return SILC_FSM_CONTINUE;
2906 /********************************* SERVICE **********************************/
2908 /* Command SERVICE. Negotiates service agreement with server. */
2909 /* XXX incomplete */
2911 SILC_FSM_STATE(silc_client_command_service)
2913 SilcClientCommandContext cmd = fsm_context;
2915 SilcClientConnection conn = cmd->conn;
2919 if (cmd->argc < 2) {
2920 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2921 "Usage: /SERVICE [<service name>] [-pubkey]");
2922 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2923 return SILC_FSM_FINISH;
2926 name = cmd->argv[1];
2928 /* Send SERVICE command to the server */
2929 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2930 ++conn->cmd_ident, 1,
2931 1, name, strlen(name));
2932 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2933 NULL, 0, NULL, NULL, buffer->data,
2935 silc_buffer_free(buffer);
2938 /* Notify application */
2939 COMMAND(SILC_STATUS_OK);
2941 /** Wait for command reply */
2942 silc_fsm_next(fsm, silc_client_command_reply_wait);
2943 return SILC_FSM_CONTINUE;
2946 /* Register all default commands provided by the client library for the
2949 void silc_client_commands_register(SilcClient client)
2951 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2954 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2955 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2956 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2957 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2958 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2959 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2960 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2961 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2962 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2963 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2964 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2965 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2966 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2967 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2968 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2969 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2970 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2971 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2972 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2973 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2974 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2975 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2976 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2977 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2978 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2979 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2980 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2983 /* Unregister all commands. */
2985 void silc_client_commands_unregister(SilcClient client)
2987 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2988 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2989 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2990 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2991 SILC_CLIENT_CMDU(list, LIST, "LIST");
2992 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2993 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2994 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2995 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2996 SILC_CLIENT_CMDU(info, INFO, "INFO");
2997 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2998 SILC_CLIENT_CMDU(ping, PING, "PING");
2999 SILC_CLIENT_CMDU(oper, OPER, "OPER");
3000 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
3001 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
3002 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
3003 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
3004 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3005 SILC_CLIENT_CMDU(kick, KICK, "KICK");
3006 SILC_CLIENT_CMDU(ban, BAN, "BAN");
3007 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3008 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3009 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3010 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3011 SILC_CLIENT_CMDU(users, USERS, "USERS");
3012 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3013 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3016 /****************** Client Side Incoming Command Handling *******************/
3019 SilcClientConnection conn;
3020 SilcUInt16 cmd_ident;
3021 } *SilcClientProcessWhois;
3023 /* Send reply to WHOIS from server */
3026 silc_client_command_process_whois_send(SilcBool success,
3027 const unsigned char *data,
3028 SilcUInt32 data_len, void *context)
3030 SilcClientProcessWhois w = context;
3031 SilcBufferStruct buffer;
3039 silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3041 /* Send the attributes back in COMMAND_REPLY packet */
3043 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3044 SILC_STATUS_OK, 0, w->cmd_ident,
3046 silc_buffer_len(&buffer));
3052 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3054 silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3055 silc_buffer_datalen(packet));
3057 silc_buffer_free(packet);
3061 /* Process WHOIS command from server */
3063 static void silc_client_command_process_whois(SilcClient client,
3064 SilcClientConnection conn,
3065 SilcCommandPayload payload,
3066 SilcArgumentPayload args)
3068 SilcClientProcessWhois w;
3073 SILC_LOG_DEBUG(("Received WHOIS command"));
3075 /* Try to take the Requested Attributes */
3076 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3080 attrs = silc_attribute_payload_parse(tmp, tmp_len);
3084 w = silc_calloc(1, sizeof(*w));
3086 silc_attribute_payload_list_free(attrs);
3090 w->cmd_ident = silc_command_get_ident(payload);
3092 /* Process requested attributes */
3093 silc_client_attributes_process(client, conn, attrs,
3094 silc_client_command_process_whois_send, w);
3095 silc_attribute_payload_list_free(attrs);
3098 /* Client is able to receive some command packets even though they are
3099 special case. Server may send WHOIS command to the client to retrieve
3100 Requested Attributes information for WHOIS query the server is
3101 processing. This function currently handles only the WHOIS command,
3102 but if in the future more commands may arrive then this can be made
3103 to support other commands too. */
3105 SILC_FSM_STATE(silc_client_command)
3107 SilcClientConnection conn = fsm_context;
3108 SilcClient client = conn->client;
3109 SilcPacket packet = state_context;
3110 SilcCommandPayload payload;
3111 SilcCommand command;
3112 SilcArgumentPayload args;
3114 /* Get command payload from packet */
3115 payload = silc_command_payload_parse(packet->buffer.data,
3116 silc_buffer_len(&packet->buffer));
3118 SILC_LOG_DEBUG(("Bad command packet"));
3119 return SILC_FSM_FINISH;
3123 args = silc_command_get_args(payload);
3125 /* Get the command */
3126 command = silc_command_get(payload);
3129 case SILC_COMMAND_WHOIS:
3130 /* Ignore everything if requested by application */
3131 if (conn->internal->params.ignore_requested_attributes)
3134 silc_client_command_process_whois(client, conn, payload, args);
3141 silc_command_payload_free(payload);
3142 return SILC_FSM_FINISH;