5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 /* Command operation that is called at the end of all commands.
28 Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command( \
30 cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status) \
34 cmd->conn->client->internal->ops->command(cmd->conn->client, \
35 cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args) \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name, \
40 silc_client_command_##func, \
41 silc_client_command_reply_##func, args)
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name) \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd, \
46 silc_client_command_##func, \
47 silc_client_command_reply_##func)
49 #define SAY cmd->conn->client->internal->ops->say
51 /************************ Static utility functions **************************/
53 /* Return next available command identifier. */
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
59 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
61 cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
66 /* State to finish command thread after an error in resolving command */
68 SILC_FSM_STATE(silc_client_command_continue_error)
70 /* Destructor will free all resources */
71 return SILC_FSM_FINISH;
74 /* Command reply callback to continue with the execution of a command.
75 This will continue when first successful reply is received, and ignores
76 the rest. On the other hand, if only errors are received it will
77 wait for all errors before continuing. */
79 static SilcBool silc_client_command_continue(SilcClient client,
80 SilcClientConnection conn,
87 SilcClientCommandContext cmd = context;
89 /* Continue immediately when successful reply is received */
90 if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91 SILC_FSM_CALL_CONTINUE(&cmd->thread);
98 /* Continue after last error is received */
99 if (SILC_STATUS_IS_ERROR(status) ||
100 (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102 SILC_FSM_CALL_CONTINUE(&cmd->thread);
109 /* Continues after resolving completed. */
111 static void silc_client_command_resolve_continue(SilcClient client,
112 SilcClientConnection conn,
117 SilcClientCommandContext cmd = context;
119 if (status != SILC_STATUS_OK)
120 silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
122 /* Continue with the command */
123 SILC_FSM_CALL_CONTINUE(&cmd->thread);
126 /* Dummy command callback. Nothing interesting to do here. Use this when
127 you just send command but don't care about reply. */
129 SilcBool silc_client_command_called_dummy(SilcClient client,
130 SilcClientConnection conn,
140 /* Dummy resolving callback. Nothing interesting to do here. Use this
141 when you just resolve entires but don't care about reply. */
143 void silc_client_command_resolve_dummy(SilcClient client,
144 SilcClientConnection conn,
152 /* Register command to client */
155 silc_client_command_register(SilcClient client,
158 SilcFSMStateCallback command_func,
159 SilcFSMStateCallback command_reply_func,
162 SilcClientCommand cmd;
164 cmd = silc_calloc(1, sizeof(*cmd));
168 cmd->command = command_func;
169 cmd->reply = command_reply_func;
170 cmd->max_args = max_args;
171 cmd->name = name ? strdup(name) : NULL;
177 silc_list_add(client->internal->commands, cmd);
182 /* Unregister command from client */
185 silc_client_command_unregister(SilcClient client,
187 SilcFSMStateCallback command_func,
188 SilcFSMStateCallback command_reply_func)
190 SilcClientCommand cmd;
192 silc_list_start(client->internal->commands);
193 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
194 if (cmd->cmd == command && cmd->command == command_func &&
195 cmd->reply == command_reply_func) {
196 silc_list_del(client->internal->commands, cmd);
197 silc_free(cmd->name);
206 /* Finds and returns a pointer to the command list. Return NULL if the
207 command is not found. */
209 static SilcClientCommand silc_client_command_find(SilcClient client,
212 SilcClientCommand cmd;
214 silc_list_start(client->internal->commands);
215 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
216 if (cmd->name && !strcasecmp(cmd->name, name))
223 /* Command thread destructor */
225 static void silc_client_command_destructor(SilcFSMThread thread,
227 void *destructor_context)
229 SilcClientCommandContext cmd = fsm_context;
230 SilcClientConnection conn = cmd->conn;
232 /* Removes commands that aren't waiting for reply but are waiting
233 for something. They may not have been removed yet. */
234 silc_list_del(conn->internal->pending_commands, cmd);
236 silc_client_command_free(cmd);
239 /* Add a command pending a command reply. Used internally by the library. */
242 silc_client_command_add_pending(SilcClientConnection conn,
243 SilcClientCommandContext cmd,
244 SilcClientCommandReply reply,
247 SilcClientCommandReplyCallback cb;
249 silc_mutex_lock(conn->internal->lock);
251 /* Add pending callback, if defined */
253 cb = silc_calloc(1, sizeof(*cb));
255 silc_mutex_unlock(conn->internal->lock);
259 cb->context = context;
260 silc_list_add(cmd->reply_callbacks, cb);
263 /* Add pending reply */
264 silc_list_add(conn->internal->pending_commands, cmd);
266 silc_mutex_unlock(conn->internal->lock);
271 /* Generic function to send any command. The arguments must be sent already
272 encoded into correct format and in correct order. Arguments come from
273 variable argument list pointer. */
275 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
276 SilcClientConnection conn,
277 SilcClientCommandContext cmd,
279 SilcClientCommandReply reply,
281 SilcUInt32 argc, va_list ap)
285 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
287 if (conn->internal->disconnected)
291 cmd->cmd_ident = silc_client_cmd_ident(conn);
293 /* Encode command payload */
294 packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
298 /* Send the command */
299 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
300 silc_buffer_datalen(packet))) {
301 silc_buffer_free(packet);
305 /* Add the command pending command reply */
306 silc_client_command_add_pending(conn, cmd, reply, reply_context);
308 silc_buffer_free(packet);
310 return cmd->cmd_ident;
313 /* Generic function to send any command. The arguments must be sent already
314 encoded into correct format and in correct order. Arguments come from
318 silc_client_command_send_arg_array(SilcClient client,
319 SilcClientConnection conn,
320 SilcClientCommandContext cmd,
322 SilcClientCommandReply reply,
325 unsigned char **argv,
326 SilcUInt32 *argv_lens,
327 SilcUInt32 *argv_types)
331 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
333 if (conn->internal->disconnected)
337 cmd->cmd_ident = silc_client_cmd_ident(conn);
339 /* Encode command payload */
340 packet = silc_command_payload_encode(command, argc, argv, argv_lens,
341 argv_types, cmd->cmd_ident);
345 /* Send the command */
346 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
347 silc_buffer_datalen(packet))) {
348 silc_buffer_free(packet);
352 /* Add the command pending command reply */
353 silc_client_command_add_pending(conn, cmd, reply, reply_context);
355 silc_buffer_free(packet);
357 return cmd->cmd_ident;
360 /* Generic function to send any command. The arguments must be sent already
361 encoded into correct format and in correct order. This is used internally
364 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
365 SilcClientCommandContext cmd,
367 SilcClientCommandReply reply,
369 SilcUInt32 argc, ...)
372 SilcUInt16 cmd_ident;
375 cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
376 reply, reply_context, argc, ap);
382 /****************************** Command API *********************************/
384 /* Free command context and its internals */
386 void silc_client_command_free(SilcClientCommandContext cmd)
388 SilcClientCommandReplyCallback cb;
391 for (i = 0; i < cmd->argc; i++)
392 silc_free(cmd->argv[i]);
393 silc_free(cmd->argv);
394 silc_free(cmd->argv_lens);
395 silc_free(cmd->argv_types);
397 silc_list_start(cmd->reply_callbacks);
398 while ((cb = silc_list_get(cmd->reply_callbacks)))
404 /* Executes a command */
406 SilcUInt16 silc_client_command_call(SilcClient client,
407 SilcClientConnection conn,
408 const char *command_line, ...)
412 unsigned char **argv = NULL;
413 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
414 SilcClientCommand command;
415 SilcClientCommandContext cmd;
419 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
420 "You are not connected to a server, please connect to server");
424 /* Parse arguments */
425 va_start(va, command_line);
429 /* Get command name */
430 command_name = silc_memdup(command_line, strcspn(command_line, " "));
434 /* Find command by name */
435 command = silc_client_command_find(client, command_name);
437 silc_free(command_name);
441 /* Parse command line */
442 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
443 &argv_types, &argc, command->max_args);
445 silc_free(command_name);
447 arg = va_arg(va, char *);
451 /* Find command by name */
452 command = silc_client_command_find(client, arg);
457 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
458 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
459 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
460 if (!argv || !argv_lens || !argv_types)
462 argv[argc] = silc_memdup(arg, strlen(arg));
465 argv_lens[argc] = strlen(arg);
466 argv_types[argc] = argc;
468 arg = va_arg(va, char *);
473 /* Allocate command context */
474 cmd = silc_calloc(1, sizeof(*cmd));
478 cmd->cmd = command->cmd;
481 cmd->argv_lens = argv_lens;
482 cmd->argv_types = argv_types;
483 cmd->cmd_ident = silc_client_cmd_ident(conn);
486 silc_list_init(cmd->reply_callbacks,
487 struct SilcClientCommandReplyCallbackStruct, next);
490 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
491 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
492 silc_client_command_destructor, NULL, FALSE);
493 silc_fsm_start_sync(&cmd->thread, command->command);
495 return cmd->cmd_ident;
498 /* Generic function to send any command. The arguments must be sent already
499 encoded into correct format and in correct order. */
501 SilcUInt16 silc_client_command_send(SilcClient client,
502 SilcClientConnection conn,
504 SilcClientCommandReply reply,
506 SilcUInt32 argc, ...)
508 SilcClientCommandContext cmd;
514 /* Allocate command context */
515 cmd = silc_calloc(1, sizeof(*cmd));
520 silc_list_init(cmd->reply_callbacks,
521 struct SilcClientCommandReplyCallbackStruct, next);
523 /* Send the command */
526 silc_client_command_send_vap(client, conn, cmd, command, reply,
527 reply_context, argc, ap);
530 if (!cmd->cmd_ident) {
531 silc_client_command_free(cmd);
535 /*** Wait for command reply */
536 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
537 silc_client_command_destructor, NULL, FALSE);
538 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
540 return cmd->cmd_ident;
543 /* Generic function to send any command. The arguments must be sent already
544 encoded into correct format and in correct order. Arguments come from
547 SilcUInt16 silc_client_command_send_argv(SilcClient client,
548 SilcClientConnection conn,
550 SilcClientCommandReply reply,
553 unsigned char **argv,
554 SilcUInt32 *argv_lens,
555 SilcUInt32 *argv_types)
557 SilcClientCommandContext cmd;
562 /* Allocate command context */
563 cmd = silc_calloc(1, sizeof(*cmd));
569 /* Send the command */
571 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
572 reply_context, argc, argv, argv_lens,
574 if (!cmd->cmd_ident) {
575 silc_client_command_free(cmd);
579 /*** Wait for command reply */
580 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
581 silc_client_command_destructor, NULL, FALSE);
582 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
584 return cmd->cmd_ident;
587 /* Attach to a command and command identifier to receive command reply. */
589 SilcBool silc_client_command_pending(SilcClientConnection conn,
592 SilcClientCommandReply reply,
595 SilcClientCommandContext cmd;
596 SilcClientCommandReplyCallback cb;
601 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
603 silc_mutex_lock(conn->internal->lock);
605 /* Find the pending command */
606 silc_list_start(conn->internal->pending_commands);
607 while ((cmd = silc_list_get(conn->internal->pending_commands)))
608 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
609 && cmd->cmd_ident == ident) {
611 /* Add the callback */
612 cb = silc_calloc(1, sizeof(*cb));
616 cb->context = context;
617 silc_list_add(cmd->reply_callbacks, cb);
620 silc_mutex_unlock(conn->internal->lock);
625 /******************************** WHOIS *************************************/
627 /* Command WHOIS. This command is used to query information about
630 SILC_FSM_STATE(silc_client_command_whois)
632 SilcClientCommandContext cmd = fsm_context;
633 SilcClientConnection conn = cmd->conn;
634 SilcClient client = conn->client;
635 SilcBuffer attrs = NULL;
636 unsigned char count[4], *tmp = NULL;
637 SilcBool details = FALSE, nick = FALSE;
638 unsigned char *pubkey = NULL;
639 char *nickname = NULL;
642 /* Given without arguments fetches client's own information */
644 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
645 silc_buffer_data(conn->internal->local_idp),
646 silc_buffer_len(conn->internal->local_idp));
648 /* Notify application */
649 COMMAND(SILC_STATUS_OK);
651 /** Wait for command reply */
652 silc_fsm_next(fsm, silc_client_command_reply_wait);
653 return SILC_FSM_CONTINUE;
656 for (i = 1; i < cmd->argc; i++) {
657 if (!strcasecmp(cmd->argv[i], "-details")) {
659 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
660 pubkey = cmd->argv[i + 1];
663 /* We assume that the first parameter is the nickname, if it isn't
664 -details or -pubkey. The last parameter should always be the count */
667 } else if (i == cmd->argc - 1) {
668 int c = atoi(cmd->argv[i]);
669 SILC_PUT32_MSB(c, count);
676 /* If pubkey is set, add all attributes to the attrs buffer, except
679 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
680 SILC_ATTRIBUTE_SERVICE,
681 SILC_ATTRIBUTE_STATUS_MOOD,
682 SILC_ATTRIBUTE_STATUS_FREETEXT,
683 SILC_ATTRIBUTE_STATUS_MESSAGE,
684 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
685 SILC_ATTRIBUTE_PREFERRED_CONTACT,
686 SILC_ATTRIBUTE_TIMEZONE,
687 SILC_ATTRIBUTE_GEOLOCATION,
688 SILC_ATTRIBUTE_DEVICE_INFO,
689 SILC_ATTRIBUTE_USER_ICON, 0);
691 attrs = silc_client_attributes_request(0);
696 SilcAttributeObjPk obj;
699 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
700 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
701 "Could not load public key %s, check the filename",
703 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
707 switch (silc_pkcs_get_type(pk)) {
709 obj.type = "silc-rsa";
712 obj.type = "ssh-rsa";
714 case SILC_PKCS_X509V3:
715 obj.type = "x509v3-sign-rsa";
717 case SILC_PKCS_OPENPGP:
718 obj.type = "pgp-sign-rsa";
724 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
726 attrs = silc_attribute_payload_encode(attrs,
727 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
728 SILC_ATTRIBUTE_FLAG_VALID,
734 silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname);
736 nickname = strdup(cmd->argv[1]);
740 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
741 3, 1, nick ? nickname : NULL,
742 nick ? strlen(nickname) : 0,
743 2, tmp ? tmp : NULL, tmp ? 4 : 0,
744 3, silc_buffer_datalen(attrs));
747 /* Notify application */
748 COMMAND(SILC_STATUS_OK);
750 /** Wait for command reply */
751 silc_fsm_next(fsm, silc_client_command_reply_wait);
752 return SILC_FSM_CONTINUE;
755 return SILC_FSM_FINISH;
758 /******************************** WHOWAS ************************************/
760 /* Command WHOWAS. This command is used to query history information about
761 specific user that used to exist in the network. */
763 SILC_FSM_STATE(silc_client_command_whowas)
765 SilcClientCommandContext cmd = fsm_context;
766 SilcClientConnection conn = cmd->conn;
767 unsigned char count[4];
770 if (cmd->argc < 2 || cmd->argc > 3) {
771 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
772 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
773 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
774 SILC_STATUS_ERR_TOO_MANY_PARAMS));
775 return SILC_FSM_FINISH;
778 if (cmd->argc == 2) {
779 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
780 1, 1, cmd->argv[1], cmd->argv_lens[1]);
782 c = atoi(cmd->argv[2]);
783 SILC_PUT32_MSB(c, count);
784 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
785 2, 1, cmd->argv[1], cmd->argv_lens[1],
786 2, count, sizeof(count));
789 /* Notify application */
790 COMMAND(SILC_STATUS_OK);
792 /** Wait for command reply */
793 silc_fsm_next(fsm, silc_client_command_reply_wait);
794 return SILC_FSM_CONTINUE;
797 /******************************** IDENTIFY **********************************/
799 /* Command IDENTIFY. This command is used to query information about
800 specific user, especially ID's. */
802 SILC_FSM_STATE(silc_client_command_identify)
804 SilcClientCommandContext cmd = fsm_context;
805 SilcClientConnection conn = cmd->conn;
806 unsigned char count[4];
809 if (cmd->argc < 2 || cmd->argc > 3)
810 return SILC_FSM_FINISH;
812 if (cmd->argc == 2) {
813 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
814 1, 1, cmd->argv[1], cmd->argv_lens[1]);
816 c = atoi(cmd->argv[2]);
817 SILC_PUT32_MSB(c, count);
818 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
819 2, 1, cmd->argv[1], cmd->argv_lens[1],
820 4, count, sizeof(count));
823 /** Wait for command reply */
824 silc_fsm_next(fsm, silc_client_command_reply_wait);
825 return SILC_FSM_CONTINUE;
828 /********************************** NICK ************************************/
830 /* Command NICK. Shows current nickname/sets new nickname on current
833 SILC_FSM_STATE(silc_client_command_nick)
835 SilcClientCommandContext cmd2, cmd = fsm_context;
836 SilcClientConnection conn = cmd->conn;
839 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
840 "Usage: /NICK <nickname>");
841 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
845 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
848 /* Show current nickname */
851 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
852 "Your nickname is %s on server %s",
853 conn->local_entry->nickname, conn->remote_host);
855 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
856 "Your nickname is %s", conn->local_entry->nickname);
859 COMMAND(SILC_STATUS_OK);
863 /* If JOIN command is active, wait for it to finish before sending NICK.
864 To avoid problems locally with changing IDs while joining, we do this. */
865 silc_mutex_lock(conn->internal->lock);
866 silc_list_start(conn->internal->pending_commands);
867 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
868 if (cmd2->cmd == SILC_COMMAND_JOIN) {
869 silc_mutex_unlock(conn->internal->lock);
870 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
871 return SILC_FSM_WAIT;
874 silc_mutex_unlock(conn->internal->lock);
876 if (cmd->argv_lens[1] > 128)
877 cmd->argv_lens[1] = 128;
879 /* Send the NICK command */
880 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
881 1, 1, cmd->argv[1], cmd->argv_lens[1]);
883 /* Notify application */
884 COMMAND(SILC_STATUS_OK);
886 /** Wait for command reply */
887 silc_fsm_next(fsm, silc_client_command_reply_wait);
888 return SILC_FSM_CONTINUE;
891 return SILC_FSM_FINISH;
894 /********************************** LIST ************************************/
896 /* Command LIST. Lists channels on the current server. */
898 SILC_FSM_STATE(silc_client_command_list)
900 SilcClientCommandContext cmd = fsm_context;
901 SilcClientConnection conn = cmd->conn;
902 SilcClient client = conn->client;
903 SilcChannelEntry channel = NULL;
904 SilcBuffer idp = NULL;
906 if (cmd->argc == 2) {
907 /* Get the Channel ID of the channel */
908 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
910 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
914 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
916 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
917 1, 1, silc_buffer_datalen(idp));
919 silc_buffer_free(idp);
920 silc_client_unref_channel(client, conn, channel);
922 /* Notify application */
923 COMMAND(SILC_STATUS_OK);
925 /** Wait for command reply */
926 silc_fsm_next(fsm, silc_client_command_reply_wait);
927 return SILC_FSM_CONTINUE;
930 /********************************** TOPIC ***********************************/
932 /* Command TOPIC. Sets/shows topic on a channel. */
934 SILC_FSM_STATE(silc_client_command_topic)
936 SilcClientCommandContext cmd = fsm_context;
937 SilcClientConnection conn = cmd->conn;
938 SilcClient client = conn->client;
939 SilcChannelEntry channel;
941 char *name, tmp[512];
943 if (cmd->argc < 2 || cmd->argc > 3) {
944 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
945 "Usage: /TOPIC <channel> [<topic>]");
946 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
947 SILC_STATUS_ERR_TOO_MANY_PARAMS));
951 if (cmd->argv[1][0] == '*') {
952 if (!conn->current_channel) {
953 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
957 if (client->internal->params->full_channel_names)
958 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
960 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
961 conn->current_channel->channel_name,
962 conn->current_channel->server[0] ? "@" : "",
963 conn->current_channel->server);
969 if (!conn->current_channel) {
970 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
974 /* Get the Channel ID of the channel */
975 channel = silc_client_get_channel(conn->client, conn, name);
977 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
981 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
983 /* Send TOPIC command to the server */
985 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
986 1, silc_buffer_datalen(idp),
987 2, cmd->argv[2], strlen(cmd->argv[2]));
989 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
990 1, silc_buffer_datalen(idp));
992 silc_buffer_free(idp);
993 silc_client_unref_channel(client, conn, channel);
995 /* Notify application */
996 COMMAND(SILC_STATUS_OK);
998 /** Wait for command reply */
999 silc_fsm_next(fsm, silc_client_command_reply_wait);
1000 return SILC_FSM_CONTINUE;
1003 return SILC_FSM_FINISH;
1006 /********************************* INVITE ***********************************/
1008 /* Command INVITE. Invites specific client to join a channel. This is
1009 also used to mange the invite list of the channel. */
1011 SILC_FSM_STATE(silc_client_command_invite)
1013 SilcClientCommandContext cmd = fsm_context;
1014 SilcClientConnection conn = cmd->conn;
1015 SilcClient client = conn->client;
1016 SilcClientEntry client_entry = NULL;
1017 SilcChannelEntry channel = NULL;
1018 SilcBuffer clidp, chidp, args = NULL;
1019 SilcPublicKey pubkey = NULL;
1020 SilcDList clients = NULL;
1021 char *nickname = NULL, *name;
1022 char *invite = NULL;
1023 unsigned char action[1];
1025 if (cmd->argc < 2) {
1026 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1027 "Usage: /INVITE <channel> [<nickname>[@server>]"
1028 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1029 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1033 if (cmd->argv[1][0] == '*') {
1034 if (!conn->current_channel) {
1035 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1039 channel = conn->current_channel;
1040 silc_client_ref_channel(client, conn, channel);
1042 name = cmd->argv[1];
1044 channel = silc_client_get_channel(conn->client, conn, name);
1046 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1051 /* Parse the typed nickname. */
1052 if (cmd->argc == 3) {
1053 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1054 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1056 /* Find client entry */
1057 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1060 /* Resolve client information */
1061 SILC_FSM_CALL(silc_client_get_clients(
1062 client, conn, nickname, NULL,
1063 silc_client_command_resolve_continue,
1066 client_entry = silc_dlist_get(clients);
1068 if (cmd->argv[2][0] == '+')
1073 /* Check if it is public key file to be added to invite list */
1074 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1075 invite = cmd->argv[2];
1082 args = silc_buffer_alloc_size(2);
1083 silc_buffer_format(args,
1084 SILC_STR_UI_SHORT(1),
1087 chidp = silc_public_key_payload_encode(pubkey);
1088 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1089 silc_buffer_len(chidp), 2);
1090 silc_buffer_free(chidp);
1091 silc_pkcs_public_key_free(pubkey);
1093 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1097 /* Send the command */
1098 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1100 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1101 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1102 1, silc_buffer_datalen(chidp),
1103 2, silc_buffer_datalen(clidp),
1104 3, args ? action : NULL, args ? 1 : 0,
1105 4, silc_buffer_datalen(args));
1106 silc_buffer_free(clidp);
1108 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1109 1, silc_buffer_datalen(chidp),
1110 3, args ? action : NULL, args ? 1 : 0,
1111 4, silc_buffer_datalen(args));
1114 silc_buffer_free(chidp);
1115 silc_buffer_free(args);
1116 silc_free(nickname);
1117 silc_client_list_free(client, conn, clients);
1118 silc_client_unref_channel(client, conn, channel);
1120 /* Notify application */
1121 COMMAND(SILC_STATUS_OK);
1123 /** Wait for command reply */
1124 silc_fsm_next(fsm, silc_client_command_reply_wait);
1125 return SILC_FSM_CONTINUE;
1128 silc_free(nickname);
1129 return SILC_FSM_FINISH;
1132 /********************************** QUIT ************************************/
1134 /* Close the connection */
1136 SILC_FSM_STATE(silc_client_command_quit_final)
1138 SilcClientCommandContext cmd = fsm_context;
1139 SilcClientConnection conn = cmd->conn;
1141 SILC_LOG_DEBUG(("Quitting"));
1143 /* Notify application */
1144 COMMAND(SILC_STATUS_OK);
1146 /* Signal to close connection */
1147 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1148 if (!conn->internal->disconnected) {
1149 conn->internal->disconnected = TRUE;
1150 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1153 return SILC_FSM_FINISH;
1156 /* Command QUIT. Closes connection with current server. */
1158 SILC_FSM_STATE(silc_client_command_quit)
1160 SilcClientCommandContext cmd = fsm_context;
1161 SilcClientConnection conn = cmd->conn;
1164 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1165 1, cmd->argv[1], cmd->argv_lens[1]);
1167 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1169 /* Sleep for a while */
1172 /* We close the connection with a little timeout */
1173 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1174 return SILC_FSM_WAIT;
1177 /********************************** KILL ************************************/
1179 /* Command KILL. Router operator can use this command to remove an client
1180 fromthe SILC Network. */
1182 SILC_FSM_STATE(silc_client_command_kill)
1184 SilcClientCommandContext cmd = fsm_context;
1185 SilcClientConnection conn = cmd->conn;
1186 SilcClient client = conn->client;
1187 SilcBuffer idp, auth = NULL;
1188 SilcClientEntry target;
1190 char *nickname = NULL, *comment = NULL;
1192 if (cmd->argc < 2) {
1193 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1194 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1195 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1196 return SILC_FSM_FINISH;
1199 /* Parse the typed nickname. */
1200 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1201 return SILC_FSM_FINISH;
1203 /* Get the target client */
1204 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1206 /* Resolve client information */
1207 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1208 silc_client_command_resolve_continue,
1211 target = silc_dlist_get(clients);
1213 if (cmd->argc >= 3) {
1214 if (strcasecmp(cmd->argv[2], "-pubkey"))
1215 comment = cmd->argv[2];
1217 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1218 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1219 /* Encode the public key authentication payload */
1220 auth = silc_auth_public_key_auth_generate(conn->public_key,
1223 conn->internal->sha1hash,
1224 &target->id, SILC_ID_CLIENT);
1228 /* Send the KILL command to the server */
1229 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1230 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1231 1, silc_buffer_datalen(idp),
1232 2, comment, comment ? strlen(comment) : 0,
1233 3, silc_buffer_datalen(auth));
1234 silc_buffer_free(idp);
1235 silc_buffer_free(auth);
1236 silc_free(nickname);
1237 silc_client_list_free(client, conn, clients);
1239 /* Notify application */
1240 COMMAND(SILC_STATUS_OK);
1242 /** Wait for command reply */
1243 silc_fsm_next(fsm, silc_client_command_reply_wait);
1244 return SILC_FSM_CONTINUE;
1247 /********************************** INFO ************************************/
1249 /* Command INFO. Request information about specific server. If specific
1250 server is not provided the current server is used. */
1252 SILC_FSM_STATE(silc_client_command_info)
1254 SilcClientCommandContext cmd = fsm_context;
1255 SilcClientConnection conn = cmd->conn;
1257 /* Send the command */
1259 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1260 1, cmd->argv[1], cmd->argv_lens[1]);
1262 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1264 /* Notify application */
1265 COMMAND(SILC_STATUS_OK);
1267 /** Wait for command reply */
1268 silc_fsm_next(fsm, silc_client_command_reply_wait);
1269 return SILC_FSM_CONTINUE;
1272 /********************************** STATS ***********************************/
1274 /* Command STATS. Shows server and network statistics. */
1276 SILC_FSM_STATE(silc_client_command_stats)
1278 SilcClientCommandContext cmd = fsm_context;
1279 SilcClientConnection conn = cmd->conn;
1281 /* Send the command */
1282 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1283 1, silc_buffer_datalen(conn->internal->
1286 /* Notify application */
1287 COMMAND(SILC_STATUS_OK);
1289 /** Wait for command reply */
1290 silc_fsm_next(fsm, silc_client_command_reply_wait);
1291 return SILC_FSM_CONTINUE;
1294 /********************************** PING ************************************/
1296 /* Command PING. Sends ping to server. */
1298 SILC_FSM_STATE(silc_client_command_ping)
1300 SilcClientCommandContext cmd = fsm_context;
1301 SilcClientConnection conn = cmd->conn;
1303 if (cmd->argc < 2) {
1304 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1305 return SILC_FSM_FINISH;
1308 /* Send the command */
1309 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1310 1, silc_buffer_datalen(conn->internal->
1313 /* Save ping time */
1314 cmd->context = SILC_64_TO_PTR(silc_time());
1316 /* Notify application */
1317 COMMAND(SILC_STATUS_OK);
1319 /** Wait for command reply */
1320 silc_fsm_next(fsm, silc_client_command_reply_wait);
1321 return SILC_FSM_CONTINUE;
1324 /********************************** JOIN ************************************/
1326 /* Command JOIN. Joins to a channel. */
1328 SILC_FSM_STATE(silc_client_command_join)
1330 SilcClientCommandContext cmd2, cmd = fsm_context;
1331 SilcClientConnection conn = cmd->conn;
1332 SilcClient client = conn->client;
1333 SilcChannelEntry channel = NULL;
1334 SilcBuffer auth = NULL, cauth = NULL;
1335 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1336 int i, passphrase_len = 0;
1338 if (cmd->argc < 2) {
1339 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1343 /* See if we have joined to the requested channel already */
1344 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1345 if (channel && silc_client_on_channel(channel, conn->local_entry))
1348 /* If NICK command is active, wait for it to finish before sending JOIN.
1349 To avoid problems locally with changing IDs while joining, we do this. */
1350 silc_mutex_lock(conn->internal->lock);
1351 silc_list_start(conn->internal->pending_commands);
1352 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1353 if (cmd2->cmd == SILC_COMMAND_NICK) {
1354 silc_mutex_unlock(conn->internal->lock);
1355 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1356 return SILC_FSM_WAIT;
1359 silc_mutex_unlock(conn->internal->lock);
1361 if (cmd->argv_lens[1] > 256)
1362 cmd->argv_lens[1] = 256;
1364 name = cmd->argv[1];
1366 for (i = 2; i < cmd->argc; i++) {
1367 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1368 cipher = cmd->argv[++i];
1369 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1370 hmac = cmd->argv[++i];
1371 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1372 auth = silc_auth_public_key_auth_generate(conn->public_key,
1375 conn->internal->sha1hash,
1378 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1379 SilcPublicKey pubkey = conn->public_key;
1380 SilcPrivateKey privkey = conn->private_key;
1381 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1384 if (cmd->argc >= i + 3) {
1386 if (cmd->argc >= i + 4) {
1387 pass = cmd->argv[i + 3];
1390 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1391 &pubkey, &privkey)) {
1392 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1393 "Could not load key pair, check your arguments");
1394 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1400 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1401 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1403 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1404 memcpy(pubdata, pkhash, 20);
1405 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1407 conn->internal->sha1hash,
1410 memset(pubdata, 0, 128);
1413 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1414 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1415 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1416 cmd->argv_lens[i], 0);
1417 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1418 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1419 0, pu8, passphrase_len);
1422 passphrase = strdup(cmd->argv[i]);
1423 passphrase_len = cmd->argv_lens[i];
1428 /* Send JOIN command to the server */
1429 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1430 1, name, strlen(name),
1431 2, silc_buffer_datalen(conn->internal->
1433 3, passphrase, passphrase_len,
1434 4, cipher, cipher ? strlen(cipher) : 0,
1435 5, hmac, hmac ? strlen(hmac) : 0,
1436 6, silc_buffer_datalen(auth),
1437 7, silc_buffer_datalen(cauth));
1439 silc_buffer_free(auth);
1440 silc_buffer_free(cauth);
1442 memset(passphrase, 0, strlen(passphrase));
1443 silc_free(passphrase);
1444 silc_client_unref_channel(client, conn, channel);
1446 /* Notify application */
1447 COMMAND(SILC_STATUS_OK);
1449 /** Wait for command reply */
1450 silc_fsm_next(fsm, silc_client_command_reply_wait);
1451 return SILC_FSM_CONTINUE;
1454 silc_client_unref_channel(client, conn, channel);
1455 return SILC_FSM_FINISH;
1458 /********************************** MOTD ************************************/
1460 /* MOTD command. Requests motd from server. */
1462 SILC_FSM_STATE(silc_client_command_motd)
1464 SilcClientCommandContext cmd = fsm_context;
1465 SilcClientConnection conn = cmd->conn;
1467 if (cmd->argc < 1 || cmd->argc > 2) {
1468 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1469 "Usage: /MOTD [<server>]");
1470 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1471 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1472 return SILC_FSM_FINISH;
1475 /* Send the command */
1477 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1478 1, conn->remote_host,
1479 strlen(conn->remote_host));
1481 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1482 1, cmd->argv[1], cmd->argv_lens[1]);
1484 /* Notify application */
1485 COMMAND(SILC_STATUS_OK);
1487 /** Wait for command reply */
1488 silc_fsm_next(fsm, silc_client_command_reply_wait);
1489 return SILC_FSM_CONTINUE;
1492 /********************************** UMODE ***********************************/
1494 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1495 modes as client cannot set itself server/router operator privileges. */
1497 SILC_FSM_STATE(silc_client_command_umode)
1499 SilcClientCommandContext cmd = fsm_context;
1500 SilcClientConnection conn = cmd->conn;
1501 unsigned char *cp, modebuf[4];
1502 SilcUInt32 mode, add, len;
1505 if (cmd->argc < 2) {
1506 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1507 "Usage: /UMODE +|-<modes>");
1508 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1509 return SILC_FSM_FINISH;
1512 mode = conn->local_entry->mode;
1514 /* Are we adding or removing mode */
1515 if (cmd->argv[1][0] == '-')
1521 cp = cmd->argv[1] + 1;
1523 for (i = 0; i < len; i++) {
1528 mode |= SILC_UMODE_SERVER_OPERATOR;
1529 mode |= SILC_UMODE_ROUTER_OPERATOR;
1530 mode |= SILC_UMODE_GONE;
1531 mode |= SILC_UMODE_INDISPOSED;
1532 mode |= SILC_UMODE_BUSY;
1533 mode |= SILC_UMODE_PAGE;
1534 mode |= SILC_UMODE_HYPER;
1535 mode |= SILC_UMODE_ROBOT;
1536 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1537 mode |= SILC_UMODE_REJECT_WATCHING;
1539 mode = SILC_UMODE_NONE;
1544 mode |= SILC_UMODE_SERVER_OPERATOR;
1546 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1550 mode |= SILC_UMODE_ROUTER_OPERATOR;
1552 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1556 mode |= SILC_UMODE_GONE;
1558 mode &= ~SILC_UMODE_GONE;
1562 mode |= SILC_UMODE_INDISPOSED;
1564 mode &= ~SILC_UMODE_INDISPOSED;
1568 mode |= SILC_UMODE_BUSY;
1570 mode &= ~SILC_UMODE_BUSY;
1574 mode |= SILC_UMODE_PAGE;
1576 mode &= ~SILC_UMODE_PAGE;
1580 mode |= SILC_UMODE_HYPER;
1582 mode &= ~SILC_UMODE_HYPER;
1586 mode |= SILC_UMODE_ROBOT;
1588 mode &= ~SILC_UMODE_ROBOT;
1592 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1594 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1598 mode |= SILC_UMODE_REJECT_WATCHING;
1600 mode &= ~SILC_UMODE_REJECT_WATCHING;
1604 mode |= SILC_UMODE_BLOCK_INVITE;
1606 mode &= ~SILC_UMODE_BLOCK_INVITE;
1609 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1610 return SILC_FSM_FINISH;
1615 SILC_PUT32_MSB(mode, modebuf);
1617 /* Send the command */
1618 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1619 1, silc_buffer_datalen(conn->internal->
1621 2, modebuf, sizeof(modebuf));
1623 /* Notify application */
1624 COMMAND(SILC_STATUS_OK);
1626 /** Wait for command reply */
1627 silc_fsm_next(fsm, silc_client_command_reply_wait);
1628 return SILC_FSM_CONTINUE;
1631 /********************************** CMODE ***********************************/
1633 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1634 can be set several at once. Those modes that require argument must be set
1635 separately (unless set with modes that does not require arguments). */
1637 SILC_FSM_STATE(silc_client_command_cmode)
1639 SilcClientCommandContext cmd = fsm_context;
1640 SilcClientConnection conn = cmd->conn;
1641 SilcClient client = conn->client;
1642 SilcChannelEntry channel = NULL;
1643 SilcBuffer chidp, auth = NULL, pk = NULL;
1644 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1645 SilcUInt32 mode, add, type, len, arg_len = 0;
1648 if (cmd->argc < 3) {
1649 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1650 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1651 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1655 if (cmd->argv[1][0] == '*') {
1656 if (!conn->current_channel) {
1657 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1661 channel = conn->current_channel;
1662 silc_client_ref_channel(client, conn, channel);
1664 name = cmd->argv[1];
1666 channel = silc_client_get_channel(conn->client, conn, name);
1668 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1673 mode = channel->mode;
1675 /* Are we adding or removing mode */
1676 if (cmd->argv[2][0] == '-')
1681 /* Argument type to be sent to server */
1685 cp = cmd->argv[2] + 1;
1687 for (i = 0; i < len; i++) {
1691 mode |= SILC_CHANNEL_MODE_PRIVATE;
1693 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1697 mode |= SILC_CHANNEL_MODE_SECRET;
1699 mode &= ~SILC_CHANNEL_MODE_SECRET;
1703 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1705 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1709 mode |= SILC_CHANNEL_MODE_INVITE;
1711 mode &= ~SILC_CHANNEL_MODE_INVITE;
1715 mode |= SILC_CHANNEL_MODE_TOPIC;
1717 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1721 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1723 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1727 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1729 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1734 mode |= SILC_CHANNEL_MODE_ULIMIT;
1736 if (cmd->argc < 4) {
1737 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1738 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1739 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1742 ll = atoi(cmd->argv[3]);
1743 SILC_PUT32_MSB(ll, tmp);
1747 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1752 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1754 if (cmd->argc < 4) {
1755 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1756 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1757 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1761 arg_len = cmd->argv_lens[3];
1763 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1768 mode |= SILC_CHANNEL_MODE_CIPHER;
1770 if (cmd->argc < 4) {
1771 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1772 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1773 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1777 arg_len = cmd->argv_lens[3];
1779 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1784 mode |= SILC_CHANNEL_MODE_HMAC;
1786 if (cmd->argc < 4) {
1787 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1788 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1789 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1793 arg_len = cmd->argv_lens[3];
1795 mode &= ~SILC_CHANNEL_MODE_HMAC;
1800 SilcPublicKey pubkey = conn->public_key;
1801 SilcPrivateKey privkey = conn->private_key;
1803 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1806 if (cmd->argc >= 5) {
1809 pass = cmd->argv[5];
1810 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1811 &pubkey, &privkey)) {
1812 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1813 "Could not load key pair, check your arguments");
1814 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1819 pk = silc_public_key_payload_encode(pubkey);
1820 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1822 conn->internal->sha1hash,
1825 arg = silc_buffer_data(auth);
1826 arg_len = silc_buffer_len(auth);
1828 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1834 SilcBool chadd = FALSE;
1835 SilcPublicKey chpk = NULL;
1837 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1840 if (cmd->argc == 3) {
1841 /* Send empty command to receive the public key list. */
1842 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1843 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1845 1, silc_buffer_datalen(chidp));
1846 silc_buffer_free(chidp);
1847 silc_client_unref_channel(client, conn, channel);
1849 /* Notify application */
1850 COMMAND(SILC_STATUS_OK);
1852 /** Wait for command reply */
1853 silc_fsm_next(fsm, silc_client_command_reply_wait);
1854 return SILC_FSM_CONTINUE;
1857 if (cmd->argc >= 4) {
1858 auth = silc_buffer_alloc_size(2);
1859 silc_buffer_format(auth,
1860 SILC_STR_UI_SHORT(cmd->argc - 3),
1864 for (k = 3; k < cmd->argc; k++) {
1865 if (cmd->argv[k][0] == '+')
1867 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1868 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1869 "Could not load public key %s, check the filename",
1871 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1872 silc_buffer_free(auth);
1877 pk = silc_public_key_payload_encode(chpk);
1878 auth = silc_argument_payload_encode_one(auth,
1879 silc_buffer_datalen(pk),
1880 chadd ? 0x00 : 0x01);
1881 silc_pkcs_public_key_free(chpk);
1882 silc_buffer_free(pk);
1887 arg = silc_buffer_data(auth);
1888 arg_len = silc_buffer_len(auth);
1890 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1894 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1900 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1901 SILC_PUT32_MSB(mode, modebuf);
1903 /* Send the command. We support sending only one mode at once that
1904 requires an argument. */
1906 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1907 1, silc_buffer_datalen(chidp),
1908 2, modebuf, sizeof(modebuf),
1910 8, silc_buffer_datalen(pk));
1912 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1913 1, silc_buffer_datalen(chidp),
1914 2, modebuf, sizeof(modebuf));
1917 silc_buffer_free(chidp);
1918 silc_buffer_free(auth);
1919 silc_buffer_free(pk);
1920 silc_client_unref_channel(client, conn, channel);
1922 /* Notify application */
1923 COMMAND(SILC_STATUS_OK);
1925 /** Wait for command reply */
1926 silc_fsm_next(fsm, silc_client_command_reply_wait);
1927 return SILC_FSM_CONTINUE;
1930 silc_client_unref_channel(client, conn, channel);
1931 return SILC_FSM_FINISH;
1934 /********************************* CUMODE ***********************************/
1936 /* CUMODE command. Changes client's mode on a channel. */
1938 SILC_FSM_STATE(silc_client_command_cumode)
1940 SilcClientCommandContext cmd = fsm_context;
1941 SilcClientConnection conn = cmd->conn;
1942 SilcClient client = conn->client;
1943 SilcChannelEntry channel = NULL;
1944 SilcChannelUser chu;
1945 SilcClientEntry client_entry;
1946 SilcBuffer clidp, chidp, auth = NULL;
1947 SilcDList clients = NULL;
1948 unsigned char *name, *cp, modebuf[4];
1949 SilcUInt32 mode = 0, add, len;
1950 char *nickname = NULL;
1953 if (cmd->argc < 4) {
1954 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1955 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1956 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1960 if (cmd->argv[1][0] == '*') {
1961 if (!conn->current_channel) {
1962 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1966 channel = conn->current_channel;
1967 silc_client_ref_channel(client, conn, channel);
1969 name = cmd->argv[1];
1971 channel = silc_client_get_channel(conn->client, conn, name);
1973 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1978 /* Parse the typed nickname. */
1979 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1981 /* Find client entry */
1982 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
1984 /* Resolve client information */
1985 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1986 silc_client_command_resolve_continue,
1989 client_entry = silc_dlist_get(clients);
1991 /* Get the current mode */
1992 chu = silc_client_on_channel(channel, client_entry);
1996 /* Are we adding or removing mode */
1997 if (cmd->argv[2][0] == '-')
2003 cp = cmd->argv[2] + 1;
2005 for (i = 0; i < len; i++) {
2009 mode |= SILC_CHANNEL_UMODE_CHANFO;
2010 mode |= SILC_CHANNEL_UMODE_CHANOP;
2011 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2012 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2013 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2015 mode = SILC_CHANNEL_UMODE_NONE;
2020 SilcPublicKey pubkey = conn->public_key;
2021 SilcPrivateKey privkey = conn->private_key;
2023 if (cmd->argc >= 6) {
2026 pass = cmd->argv[6];
2027 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2028 &pubkey, &privkey)) {
2029 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2030 "Could not load key pair, check your arguments");
2031 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2036 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
2038 conn->internal->sha1hash,
2041 mode |= SILC_CHANNEL_UMODE_CHANFO;
2043 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2048 mode |= SILC_CHANNEL_UMODE_CHANOP;
2050 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2054 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2056 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2060 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2062 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2066 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2068 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2072 mode |= SILC_CHANNEL_UMODE_QUIET;
2074 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2077 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2083 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2084 SILC_PUT32_MSB(mode, modebuf);
2085 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2087 /* Send the command packet. We support sending only one mode at once
2088 that requires an argument. */
2089 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2090 1, silc_buffer_datalen(chidp),
2092 3, silc_buffer_datalen(clidp),
2093 4, silc_buffer_datalen(auth));
2095 silc_buffer_free(chidp);
2096 silc_buffer_free(clidp);
2098 silc_buffer_free(auth);
2099 silc_free(nickname);
2100 silc_client_list_free(client, conn, clients);
2101 silc_client_unref_channel(client, conn, channel);
2103 /* Notify application */
2104 COMMAND(SILC_STATUS_OK);
2106 /** Wait for command reply */
2107 silc_fsm_next(fsm, silc_client_command_reply_wait);
2108 return SILC_FSM_CONTINUE;
2111 silc_client_unref_channel(client, conn, channel);
2112 silc_client_list_free(client, conn, clients);
2113 silc_free(nickname);
2114 return SILC_FSM_FINISH;
2117 /********************************** KICK ************************************/
2119 /* KICK command. Kicks a client out of channel. */
2121 SILC_FSM_STATE(silc_client_command_kick)
2123 SilcClientCommandContext cmd = fsm_context;
2124 SilcClientConnection conn = cmd->conn;
2125 SilcClient client = conn->client;
2126 SilcChannelEntry channel = NULL;
2127 SilcBuffer idp, idp2;
2128 SilcClientEntry target;
2129 SilcDList clients = NULL;
2130 char *name, tmp[512];
2132 if (cmd->argc < 3) {
2133 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2134 "Usage: /KICK <channel> <nickname> [<comment>]");
2135 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2139 if (cmd->argv[1][0] == '*') {
2140 if (!conn->current_channel) {
2141 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2145 if (client->internal->params->full_channel_names)
2146 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2148 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2149 conn->current_channel->channel_name,
2150 conn->current_channel->server[0] ? "@" : "",
2151 conn->current_channel->server);
2154 name = cmd->argv[1];
2157 if (!conn->current_channel) {
2158 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2162 /* Get the Channel ID of the channel */
2163 channel = silc_client_get_channel(conn->client, conn, name);
2165 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2169 /* Get the target client */
2170 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2172 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2173 "No such client: %s", cmd->argv[2]);
2174 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2177 target = silc_dlist_get(clients);
2179 /* Send KICK command to the server */
2180 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2181 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2183 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2184 1, silc_buffer_datalen(idp),
2185 2, silc_buffer_datalen(idp2));
2187 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2188 1, silc_buffer_datalen(idp),
2189 2, silc_buffer_datalen(idp2),
2190 3, cmd->argv[3], strlen(cmd->argv[3]));
2192 silc_buffer_free(idp);
2193 silc_buffer_free(idp2);
2194 silc_client_list_free(client, conn, clients);
2195 silc_client_unref_channel(client, conn, channel);
2197 /* Notify application */
2198 COMMAND(SILC_STATUS_OK);
2200 /** Wait for command reply */
2201 silc_fsm_next(fsm, silc_client_command_reply_wait);
2202 return SILC_FSM_CONTINUE;
2205 silc_client_unref_channel(client, conn, channel);
2206 return SILC_FSM_FINISH;
2209 /***************************** OPER & SILCOPER ******************************/
2212 unsigned char *passphrase;
2213 SilcUInt32 passphrase_len;
2214 } *SilcClientCommandOper;
2216 /* Ask passphrase callback */
2218 static void silc_client_command_oper_cb(const unsigned char *data,
2219 SilcUInt32 data_len, void *context)
2221 SilcClientCommandContext cmd = context;
2222 SilcClientCommandOper oper = cmd->context;
2224 if (data && data_len)
2225 oper->passphrase = silc_memdup(data, data_len);
2226 oper->passphrase_len = data_len;
2229 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2232 /* Send OPER/SILCOPER command */
2234 SILC_FSM_STATE(silc_client_command_oper_send)
2236 SilcClientCommandContext cmd = fsm_context;
2237 SilcClientConnection conn = cmd->conn;
2238 SilcClientCommandOper oper = cmd->context;
2241 if (!oper || !oper->passphrase) {
2242 /* Encode the public key authentication payload */
2243 auth = silc_auth_public_key_auth_generate(conn->public_key,
2246 conn->internal->hash,
2250 /* Encode the password authentication payload */
2251 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2252 oper->passphrase, oper->passphrase_len);
2255 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2256 1, cmd->argv[1], strlen(cmd->argv[1]),
2257 2, silc_buffer_datalen(auth));
2259 silc_buffer_clear(auth);
2260 silc_buffer_free(auth);
2262 silc_free(oper->passphrase);
2266 /* Notify application */
2267 COMMAND(SILC_STATUS_OK);
2269 /** Wait for command reply */
2270 silc_fsm_next(fsm, silc_client_command_reply_wait);
2271 return SILC_FSM_CONTINUE;
2274 /* OPER command. Used to obtain server operator privileges. */
2276 SILC_FSM_STATE(silc_client_command_oper)
2278 SilcClientCommandContext cmd = fsm_context;
2279 SilcClientConnection conn = cmd->conn;
2280 SilcClientCommandOper oper;
2282 if (cmd->argc < 2) {
2283 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2284 "Usage: /OPER <username> [-pubkey]");
2285 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2286 return SILC_FSM_FINISH;
2289 silc_fsm_next(fsm, silc_client_command_oper_send);
2291 /* Get passphrase */
2292 if (cmd->argc < 3) {
2293 oper = silc_calloc(1, sizeof(*oper));
2295 return SILC_FSM_FINISH;
2296 cmd->context = oper;
2297 SILC_FSM_CALL(conn->client->internal->
2298 ops->ask_passphrase(conn->client, conn,
2299 silc_client_command_oper_cb, cmd));
2302 return SILC_FSM_CONTINUE;
2305 /* SILCOPER command. Used to obtain router operator privileges. */
2307 SILC_FSM_STATE(silc_client_command_silcoper)
2309 SilcClientCommandContext cmd = fsm_context;
2310 SilcClientConnection conn = cmd->conn;
2311 SilcClientCommandOper oper;
2313 if (cmd->argc < 2) {
2314 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2315 "Usage: /SILCOPER <username> [-pubkey]");
2316 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2317 return SILC_FSM_FINISH;
2320 silc_fsm_next(fsm, silc_client_command_oper_send);
2322 /* Get passphrase */
2323 if (cmd->argc < 3) {
2324 oper = silc_calloc(1, sizeof(*oper));
2326 return SILC_FSM_FINISH;
2327 cmd->context = oper;
2328 SILC_FSM_CALL(conn->client->internal->
2329 ops->ask_passphrase(conn->client, conn,
2330 silc_client_command_oper_cb, cmd));
2333 return SILC_FSM_CONTINUE;
2336 /*********************************** BAN ************************************/
2338 /* Command BAN. This is used to manage the ban list of the channel. */
2340 SILC_FSM_STATE(silc_client_command_ban)
2342 SilcClientCommandContext cmd = fsm_context;
2343 SilcClientConnection conn = cmd->conn;
2344 SilcClient client = conn->client;
2345 SilcChannelEntry channel;
2346 SilcBuffer chidp, args = NULL;
2347 char *name, *ban = NULL;
2348 unsigned char action[1];
2349 SilcPublicKey pubkey = NULL;
2351 if (cmd->argc < 2) {
2352 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2353 "Usage: /BAN <channel> "
2354 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2355 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2359 if (cmd->argv[1][0] == '*') {
2360 if (!conn->current_channel) {
2361 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2365 channel = conn->current_channel;
2366 silc_client_ref_channel(client, conn, channel);
2368 name = cmd->argv[1];
2370 channel = silc_client_get_channel(conn->client, conn, name);
2372 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2377 if (cmd->argc == 3) {
2378 if (cmd->argv[2][0] == '+')
2383 /* Check if it is public key file to be added to invite list */
2384 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2391 args = silc_buffer_alloc_size(2);
2392 silc_buffer_format(args,
2393 SILC_STR_UI_SHORT(1),
2396 chidp = silc_public_key_payload_encode(pubkey);
2397 args = silc_argument_payload_encode_one(args,
2398 silc_buffer_datalen(chidp), 2);
2399 silc_buffer_free(chidp);
2400 silc_pkcs_public_key_free(pubkey);
2402 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2406 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2408 /* Send the command */
2409 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2410 1, silc_buffer_datalen(chidp),
2411 2, args ? action : NULL, args ? 1 : 0,
2412 3, silc_buffer_datalen(args));
2414 silc_buffer_free(chidp);
2415 silc_buffer_free(args);
2416 silc_client_unref_channel(client, conn, channel);
2418 /* Notify application */
2419 COMMAND(SILC_STATUS_OK);
2421 /** Wait for command reply */
2422 silc_fsm_next(fsm, silc_client_command_reply_wait);
2423 return SILC_FSM_CONTINUE;
2426 return SILC_FSM_FINISH;
2429 /********************************* DETACH ***********************************/
2431 /* Command DETACH. This is used to detach from the server */
2433 SILC_FSM_STATE(silc_client_command_detach)
2435 SilcClientCommandContext cmd = fsm_context;
2436 SilcClientConnection conn = cmd->conn;
2438 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2440 /* Notify application */
2441 COMMAND(SILC_STATUS_OK);
2443 /** Wait for command reply */
2444 silc_fsm_next(fsm, silc_client_command_reply_wait);
2445 return SILC_FSM_CONTINUE;
2448 /********************************** WATCH ***********************************/
2450 /* Command WATCH. */
2452 SILC_FSM_STATE(silc_client_command_watch)
2454 SilcClientCommandContext cmd = fsm_context;
2455 SilcClientConnection conn = cmd->conn;
2456 SilcBuffer args = NULL;
2458 const char *pubkey = NULL;
2459 SilcBool pubkey_add = TRUE;
2461 if (cmd->argc < 3) {
2462 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2466 if (!strcasecmp(cmd->argv[1], "-add")) {
2468 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2470 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2472 pubkey = cmd->argv[2] + 1;
2473 if (cmd->argv[2][0] == '-')
2476 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2484 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2485 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2486 "Could not load public key %s, check the filename", pubkey);
2487 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2491 args = silc_buffer_alloc_size(2);
2492 silc_buffer_format(args,
2493 SILC_STR_UI_SHORT(1),
2495 buffer = silc_public_key_payload_encode(pk);
2496 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2497 pubkey_add ? 0x00 : 0x01);
2498 silc_buffer_free(buffer);
2499 silc_pkcs_public_key_free(pk);
2502 /* If watching by nickname, resolve all users with that nickname so that
2503 we get their information immediately. */
2505 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2506 silc_client_command_resolve_dummy, NULL);
2508 /* Send the commmand */
2509 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2510 1, silc_buffer_datalen(conn->internal->
2512 type, pubkey ? args->data : cmd->argv[2],
2513 pubkey ? silc_buffer_len(args) :
2516 silc_buffer_free(args);
2518 /* Notify application */
2519 COMMAND(SILC_STATUS_OK);
2521 /** Wait for command reply */
2522 silc_fsm_next(fsm, silc_client_command_reply_wait);
2523 return SILC_FSM_CONTINUE;
2526 return SILC_FSM_FINISH;
2529 /********************************** LEAVE ***********************************/
2531 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2533 SILC_FSM_STATE(silc_client_command_leave)
2535 SilcClientCommandContext cmd = fsm_context;
2536 SilcClientConnection conn = cmd->conn;
2537 SilcClient client = conn->client;
2538 SilcChannelEntry channel;
2540 char *name, tmp[512];
2542 if (cmd->argc != 2) {
2543 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2544 "Usage: /LEAVE <channel>");
2545 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2549 if (cmd->argv[1][0] == '*') {
2550 if (!conn->current_channel) {
2551 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2555 if (client->internal->params->full_channel_names)
2556 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2558 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2559 conn->current_channel->channel_name,
2560 conn->current_channel->server[0] ? "@" : "",
2561 conn->current_channel->server);
2564 name = cmd->argv[1];
2567 /* Get the channel entry */
2568 channel = silc_client_get_channel(conn->client, conn, name);
2570 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2574 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2576 /* Send LEAVE command to the server */
2577 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2578 1, silc_buffer_datalen(idp));
2580 silc_buffer_free(idp);
2582 /* Notify application */
2583 COMMAND(SILC_STATUS_OK);
2585 if (conn->current_channel == channel)
2586 conn->current_channel = NULL;
2588 silc_client_unref_channel(client, conn, channel);
2590 /** Wait for command reply */
2591 silc_fsm_next(fsm, silc_client_command_reply_wait);
2592 return SILC_FSM_CONTINUE;
2595 return SILC_FSM_FINISH;
2598 /********************************** USERS ***********************************/
2600 /* Command USERS. Requests the USERS of the clients joined on requested
2603 SILC_FSM_STATE(silc_client_command_users)
2605 SilcClientCommandContext cmd = fsm_context;
2606 SilcClientConnection conn = cmd->conn;
2607 char *name, tmp[512];
2609 if (cmd->argc != 2) {
2610 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2611 "Usage: /USERS <channel>");
2612 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2616 if (cmd->argv[1][0] == '*') {
2617 if (!conn->current_channel) {
2618 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2622 if (conn->client->internal->params->full_channel_names)
2623 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2625 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2626 conn->current_channel->channel_name,
2627 conn->current_channel->server[0] ? "@" : "",
2628 conn->current_channel->server);
2631 name = cmd->argv[1];
2634 /* Send USERS command to the server */
2635 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2636 2, name, strlen(name));
2638 /* Notify application */
2639 COMMAND(SILC_STATUS_OK);
2641 /** Wait for command reply */
2642 silc_fsm_next(fsm, silc_client_command_reply_wait);
2643 return SILC_FSM_CONTINUE;
2646 return SILC_FSM_FINISH;
2649 /********************************* GETKEY ***********************************/
2651 /* Command GETKEY. Used to fetch remote client's public key. */
2653 SILC_FSM_STATE(silc_client_command_getkey)
2655 SilcClientCommandContext cmd = fsm_context;
2656 SilcClientConnection conn = cmd->conn;
2657 SilcClient client = conn->client;
2658 SilcClientEntry client_entry;
2659 SilcServerEntry server_entry;
2663 if (cmd->argc < 2) {
2664 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2665 "Usage: /GETKEY <nickname or server name>");
2666 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2667 return SILC_FSM_FINISH;
2670 /* Find client entry */
2671 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2673 /* Check whether user requested server */
2674 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2675 if (!server_entry) {
2676 if (cmd->resolved) {
2677 /* Resolving didn't find anything. We should never get here as
2678 errors are handled in the resolving callback. */
2679 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2680 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2681 return SILC_FSM_FINISH;
2684 /* No client or server exist with this name, query for both. */
2685 cmd->resolved = TRUE;
2686 SILC_FSM_CALL(silc_client_command_send(client, conn,
2687 SILC_COMMAND_IDENTIFY,
2688 silc_client_command_continue,
2691 strlen(cmd->argv[1]),
2693 strlen(cmd->argv[1])));
2696 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2697 silc_client_unref_server(client, conn, server_entry);
2699 client_entry = silc_dlist_get(clients);
2700 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2701 silc_client_list_free(client, conn, clients);
2704 /* Send the commmand */
2705 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2706 1, silc_buffer_datalen(idp));
2708 silc_buffer_free(idp);
2710 /* Notify application */
2711 COMMAND(SILC_STATUS_OK);
2713 /** Wait for command reply */
2714 silc_fsm_next(fsm, silc_client_command_reply_wait);
2715 return SILC_FSM_CONTINUE;
2718 /********************************* SERVICE **********************************/
2720 /* Command SERVICE. Negotiates service agreement with server. */
2721 /* XXX incomplete */
2723 SILC_FSM_STATE(silc_client_command_service)
2725 SilcClientCommandContext cmd = fsm_context;
2727 SilcClientConnection conn = cmd->conn;
2731 if (cmd->argc < 2) {
2732 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2733 "Usage: /SERVICE [<service name>] [-pubkey]");
2734 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2735 return SILC_FSM_FINISH;
2738 name = cmd->argv[1];
2740 /* Send SERVICE command to the server */
2741 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2742 ++conn->cmd_ident, 1,
2743 1, name, strlen(name));
2744 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2745 NULL, 0, NULL, NULL, buffer->data,
2747 silc_buffer_free(buffer);
2750 /* Notify application */
2751 COMMAND(SILC_STATUS_OK);
2753 /** Wait for command reply */
2754 silc_fsm_next(fsm, silc_client_command_reply_wait);
2755 return SILC_FSM_CONTINUE;
2758 /* Register all default commands provided by the client library for the
2761 void silc_client_commands_register(SilcClient client)
2763 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2766 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2767 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2768 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2769 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2770 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2771 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2772 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2773 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2774 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2775 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2776 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2777 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2778 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2779 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2780 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2781 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2782 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2783 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2784 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2785 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2786 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2787 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2788 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2789 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2790 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2791 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2792 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2795 /* Unregister all commands. */
2797 void silc_client_commands_unregister(SilcClient client)
2799 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2800 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2801 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2802 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2803 SILC_CLIENT_CMDU(list, LIST, "LIST");
2804 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2805 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2806 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2807 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2808 SILC_CLIENT_CMDU(info, INFO, "INFO");
2809 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2810 SILC_CLIENT_CMDU(ping, PING, "PING");
2811 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2812 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2813 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2814 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2815 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2816 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2817 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2818 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2819 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2820 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2821 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2822 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2823 SILC_CLIENT_CMDU(users, USERS, "USERS");
2824 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2825 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2828 /****************** Client Side Incoming Command Handling *******************/
2830 /* Reply to WHOIS command from server */
2832 static void silc_client_command_process_whois(SilcClient client,
2833 SilcClientConnection conn,
2834 SilcCommandPayload payload,
2835 SilcArgumentPayload args)
2840 SilcBuffer buffer, packet;
2842 SILC_LOG_DEBUG(("Received WHOIS command"));
2844 /* Try to take the Requested Attributes */
2845 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2849 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2853 /* Process requested attributes */
2854 buffer = silc_client_attributes_process(client, conn, attrs);
2856 silc_attribute_payload_list_free(attrs);
2860 /* Send the attributes back in COMMAND_REPLY packet */
2862 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2864 silc_command_get_ident(payload),
2865 1, 11, buffer->data,
2866 silc_buffer_len(buffer));
2868 silc_buffer_free(buffer);
2872 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
2874 silc_packet_send(conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
2875 silc_buffer_datalen(packet));
2877 silc_buffer_free(packet);
2878 silc_buffer_free(buffer);
2881 /* Client is able to receive some command packets even though they are
2882 special case. Server may send WHOIS command to the client to retrieve
2883 Requested Attributes information for WHOIS query the server is
2884 processing. This function currently handles only the WHOIS command,
2885 but if in the future more commands may arrive then this can be made
2886 to support other commands too. */
2888 SILC_FSM_STATE(silc_client_command)
2890 SilcClientConnection conn = fsm_context;
2891 SilcClient client = conn->client;
2892 SilcPacket packet = state_context;
2893 SilcCommandPayload payload;
2894 SilcCommand command;
2895 SilcArgumentPayload args;
2897 /* Get command payload from packet */
2898 payload = silc_command_payload_parse(packet->buffer.data,
2899 silc_buffer_len(&packet->buffer));
2901 SILC_LOG_DEBUG(("Bad command packet"));
2902 return SILC_FSM_FINISH;
2906 args = silc_command_get_args(payload);
2908 /* Get the command */
2909 command = silc_command_get(payload);
2912 case SILC_COMMAND_WHOIS:
2913 /* Ignore everything if requested by application */
2914 if (conn->internal->params.ignore_requested_attributes)
2917 silc_client_command_process_whois(client, conn, payload, args);
2924 silc_command_payload_free(payload);
2925 return SILC_FSM_FINISH;