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 SilcUInt16 cmd_ident;
415 SilcClientCommand command;
416 SilcClientCommandContext cmd;
420 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
421 "You are not connected to a server, please connect to server");
425 /* Parse arguments */
426 va_start(va, command_line);
430 /* Get command name */
431 command_name = silc_memdup(command_line, strcspn(command_line, " "));
435 /* Find command by name */
436 command = silc_client_command_find(client, command_name);
438 silc_free(command_name);
442 /* Parse command line */
443 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
444 &argv_types, &argc, command->max_args);
446 silc_free(command_name);
448 arg = va_arg(va, char *);
452 /* Find command by name */
453 command = silc_client_command_find(client, arg);
458 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
459 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
460 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
461 if (!argv || !argv_lens || !argv_types)
463 argv[argc] = silc_memdup(arg, strlen(arg));
466 argv_lens[argc] = strlen(arg);
467 argv_types[argc] = argc;
469 arg = va_arg(va, char *);
474 /* Allocate command context */
475 cmd = silc_calloc(1, sizeof(*cmd));
479 cmd->cmd = command->cmd;
482 cmd->argv_lens = argv_lens;
483 cmd->argv_types = argv_types;
484 cmd_ident = cmd->cmd_ident = silc_client_cmd_ident(conn);
487 silc_list_init(cmd->reply_callbacks,
488 struct SilcClientCommandReplyCallbackStruct, next);
491 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
492 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
493 silc_client_command_destructor, NULL, FALSE);
494 silc_fsm_start_sync(&cmd->thread, command->command);
499 /* Generic function to send any command. The arguments must be sent already
500 encoded into correct format and in correct order. */
502 SilcUInt16 silc_client_command_send(SilcClient client,
503 SilcClientConnection conn,
505 SilcClientCommandReply reply,
507 SilcUInt32 argc, ...)
509 SilcClientCommandContext cmd;
515 /* Allocate command context */
516 cmd = silc_calloc(1, sizeof(*cmd));
521 silc_list_init(cmd->reply_callbacks,
522 struct SilcClientCommandReplyCallbackStruct, next);
524 /* Send the command */
527 silc_client_command_send_vap(client, conn, cmd, command, reply,
528 reply_context, argc, ap);
531 if (!cmd->cmd_ident) {
532 silc_client_command_free(cmd);
536 /*** Wait for command reply */
537 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
538 silc_client_command_destructor, NULL, FALSE);
539 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
541 return cmd->cmd_ident;
544 /* Generic function to send any command. The arguments must be sent already
545 encoded into correct format and in correct order. Arguments come from
548 SilcUInt16 silc_client_command_send_argv(SilcClient client,
549 SilcClientConnection conn,
551 SilcClientCommandReply reply,
554 unsigned char **argv,
555 SilcUInt32 *argv_lens,
556 SilcUInt32 *argv_types)
558 SilcClientCommandContext cmd;
563 /* Allocate command context */
564 cmd = silc_calloc(1, sizeof(*cmd));
570 /* Send the command */
572 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
573 reply_context, argc, argv, argv_lens,
575 if (!cmd->cmd_ident) {
576 silc_client_command_free(cmd);
580 /*** Wait for command reply */
581 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
582 silc_client_command_destructor, NULL, FALSE);
583 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
585 return cmd->cmd_ident;
588 /* Attach to a command and command identifier to receive command reply. */
590 SilcBool silc_client_command_pending(SilcClientConnection conn,
593 SilcClientCommandReply reply,
596 SilcClientCommandContext cmd;
597 SilcClientCommandReplyCallback cb;
602 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
604 silc_mutex_lock(conn->internal->lock);
606 /* Find the pending command */
607 silc_list_start(conn->internal->pending_commands);
608 while ((cmd = silc_list_get(conn->internal->pending_commands)))
609 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
610 && cmd->cmd_ident == ident) {
612 /* Add the callback */
613 cb = silc_calloc(1, sizeof(*cb));
617 cb->context = context;
618 silc_list_add(cmd->reply_callbacks, cb);
621 silc_mutex_unlock(conn->internal->lock);
626 /******************************** WHOIS *************************************/
628 /* Command WHOIS. This command is used to query information about
631 SILC_FSM_STATE(silc_client_command_whois)
633 SilcClientCommandContext cmd = fsm_context;
634 SilcClientConnection conn = cmd->conn;
635 SilcClient client = conn->client;
636 SilcBuffer attrs = NULL;
637 unsigned char count[4], *tmp = NULL;
638 SilcBool details = FALSE, nick = FALSE;
639 unsigned char *pubkey = NULL;
640 char *nickname = NULL;
643 /* Given without arguments fetches client's own information */
645 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
646 silc_buffer_data(conn->internal->local_idp),
647 silc_buffer_len(conn->internal->local_idp));
649 /* Notify application */
650 COMMAND(SILC_STATUS_OK);
652 /** Wait for command reply */
653 silc_fsm_next(fsm, silc_client_command_reply_wait);
654 return SILC_FSM_CONTINUE;
657 for (i = 1; i < cmd->argc; i++) {
658 if (!strcasecmp(cmd->argv[i], "-details")) {
660 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
661 pubkey = cmd->argv[i + 1];
664 /* We assume that the first parameter is the nickname, if it isn't
665 -details or -pubkey. The last parameter should always be the count */
668 } else if (i == cmd->argc - 1) {
669 int c = atoi(cmd->argv[i]);
670 SILC_PUT32_MSB(c, count);
677 /* If pubkey is set, add all attributes to the attrs buffer, except
680 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
681 SILC_ATTRIBUTE_SERVICE,
682 SILC_ATTRIBUTE_STATUS_MOOD,
683 SILC_ATTRIBUTE_STATUS_FREETEXT,
684 SILC_ATTRIBUTE_STATUS_MESSAGE,
685 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
686 SILC_ATTRIBUTE_PREFERRED_CONTACT,
687 SILC_ATTRIBUTE_TIMEZONE,
688 SILC_ATTRIBUTE_GEOLOCATION,
689 SILC_ATTRIBUTE_DEVICE_INFO,
690 SILC_ATTRIBUTE_USER_ICON, 0);
692 attrs = silc_client_attributes_request(0);
697 SilcAttributeObjPk obj;
700 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
701 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
702 "Could not load public key %s, check the filename",
704 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
708 switch (silc_pkcs_get_type(pk)) {
710 obj.type = "silc-rsa";
713 obj.type = "ssh-rsa";
715 case SILC_PKCS_X509V3:
716 obj.type = "x509v3-sign-rsa";
718 case SILC_PKCS_OPENPGP:
719 obj.type = "pgp-sign-rsa";
725 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
727 attrs = silc_attribute_payload_encode(attrs,
728 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
729 SILC_ATTRIBUTE_FLAG_VALID,
735 silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname);
737 nickname = strdup(cmd->argv[1]);
741 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
742 3, 1, nick ? nickname : NULL,
743 nick ? strlen(nickname) : 0,
744 2, tmp ? tmp : NULL, tmp ? 4 : 0,
745 3, silc_buffer_datalen(attrs));
748 /* Notify application */
749 COMMAND(SILC_STATUS_OK);
751 /** Wait for command reply */
752 silc_fsm_next(fsm, silc_client_command_reply_wait);
753 return SILC_FSM_CONTINUE;
756 return SILC_FSM_FINISH;
759 /******************************** WHOWAS ************************************/
761 /* Command WHOWAS. This command is used to query history information about
762 specific user that used to exist in the network. */
764 SILC_FSM_STATE(silc_client_command_whowas)
766 SilcClientCommandContext cmd = fsm_context;
767 SilcClientConnection conn = cmd->conn;
768 unsigned char count[4];
771 if (cmd->argc < 2 || cmd->argc > 3) {
772 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
773 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
774 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
775 SILC_STATUS_ERR_TOO_MANY_PARAMS));
776 return SILC_FSM_FINISH;
779 if (cmd->argc == 2) {
780 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
781 1, 1, cmd->argv[1], cmd->argv_lens[1]);
783 c = atoi(cmd->argv[2]);
784 SILC_PUT32_MSB(c, count);
785 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
786 2, 1, cmd->argv[1], cmd->argv_lens[1],
787 2, count, sizeof(count));
790 /* Notify application */
791 COMMAND(SILC_STATUS_OK);
793 /** Wait for command reply */
794 silc_fsm_next(fsm, silc_client_command_reply_wait);
795 return SILC_FSM_CONTINUE;
798 /******************************** IDENTIFY **********************************/
800 /* Command IDENTIFY. This command is used to query information about
801 specific user, especially ID's. */
803 SILC_FSM_STATE(silc_client_command_identify)
805 SilcClientCommandContext cmd = fsm_context;
806 SilcClientConnection conn = cmd->conn;
807 unsigned char count[4];
810 if (cmd->argc < 2 || cmd->argc > 3)
811 return SILC_FSM_FINISH;
813 if (cmd->argc == 2) {
814 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
815 1, 1, cmd->argv[1], cmd->argv_lens[1]);
817 c = atoi(cmd->argv[2]);
818 SILC_PUT32_MSB(c, count);
819 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
820 2, 1, cmd->argv[1], cmd->argv_lens[1],
821 4, count, sizeof(count));
824 /** Wait for command reply */
825 silc_fsm_next(fsm, silc_client_command_reply_wait);
826 return SILC_FSM_CONTINUE;
829 /********************************** NICK ************************************/
831 /* Command NICK. Shows current nickname/sets new nickname on current
834 SILC_FSM_STATE(silc_client_command_nick)
836 SilcClientCommandContext cmd2, cmd = fsm_context;
837 SilcClientConnection conn = cmd->conn;
840 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
841 "Usage: /NICK <nickname>");
842 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
846 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
849 /* Show current nickname */
852 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
853 "Your nickname is %s on server %s",
854 conn->local_entry->nickname, conn->remote_host);
856 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
857 "Your nickname is %s", conn->local_entry->nickname);
860 COMMAND(SILC_STATUS_OK);
864 /* If JOIN command is active, wait for it to finish before sending NICK.
865 To avoid problems locally with changing IDs while joining, we do this. */
866 silc_mutex_lock(conn->internal->lock);
867 silc_list_start(conn->internal->pending_commands);
868 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
869 if (cmd2->cmd == SILC_COMMAND_JOIN) {
870 silc_mutex_unlock(conn->internal->lock);
871 silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
872 return SILC_FSM_WAIT;
875 silc_mutex_unlock(conn->internal->lock);
877 if (cmd->argv_lens[1] > 128)
878 cmd->argv_lens[1] = 128;
880 /* Send the NICK command */
881 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
882 1, 1, cmd->argv[1], cmd->argv_lens[1]);
884 /* Notify application */
885 COMMAND(SILC_STATUS_OK);
887 /** Wait for command reply */
888 silc_fsm_next(fsm, silc_client_command_reply_wait);
889 return SILC_FSM_CONTINUE;
892 return SILC_FSM_FINISH;
895 /********************************** LIST ************************************/
897 /* Command LIST. Lists channels on the current server. */
899 SILC_FSM_STATE(silc_client_command_list)
901 SilcClientCommandContext cmd = fsm_context;
902 SilcClientConnection conn = cmd->conn;
903 SilcClient client = conn->client;
904 SilcChannelEntry channel = NULL;
905 SilcBuffer idp = NULL;
907 if (cmd->argc == 2) {
908 /* Get the Channel ID of the channel */
909 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
911 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
915 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
917 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
918 1, 1, silc_buffer_datalen(idp));
920 silc_buffer_free(idp);
921 silc_client_unref_channel(client, conn, channel);
923 /* Notify application */
924 COMMAND(SILC_STATUS_OK);
926 /** Wait for command reply */
927 silc_fsm_next(fsm, silc_client_command_reply_wait);
928 return SILC_FSM_CONTINUE;
931 /********************************** TOPIC ***********************************/
933 /* Command TOPIC. Sets/shows topic on a channel. */
935 SILC_FSM_STATE(silc_client_command_topic)
937 SilcClientCommandContext cmd = fsm_context;
938 SilcClientConnection conn = cmd->conn;
939 SilcClient client = conn->client;
940 SilcChannelEntry channel;
942 char *name, tmp[512];
944 if (cmd->argc < 2 || cmd->argc > 3) {
945 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
946 "Usage: /TOPIC <channel> [<topic>]");
947 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
948 SILC_STATUS_ERR_TOO_MANY_PARAMS));
952 if (cmd->argv[1][0] == '*') {
953 if (!conn->current_channel) {
954 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
958 if (client->internal->params->full_channel_names)
959 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
961 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
962 conn->current_channel->channel_name,
963 conn->current_channel->server[0] ? "@" : "",
964 conn->current_channel->server);
970 if (!conn->current_channel) {
971 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
975 /* Get the Channel ID of the channel */
976 channel = silc_client_get_channel(conn->client, conn, name);
978 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
982 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
984 /* Send TOPIC command to the server */
986 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
987 1, silc_buffer_datalen(idp),
988 2, cmd->argv[2], strlen(cmd->argv[2]));
990 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
991 1, silc_buffer_datalen(idp));
993 silc_buffer_free(idp);
994 silc_client_unref_channel(client, conn, channel);
996 /* Notify application */
997 COMMAND(SILC_STATUS_OK);
999 /** Wait for command reply */
1000 silc_fsm_next(fsm, silc_client_command_reply_wait);
1001 return SILC_FSM_CONTINUE;
1004 return SILC_FSM_FINISH;
1007 /********************************* INVITE ***********************************/
1009 /* Command INVITE. Invites specific client to join a channel. This is
1010 also used to mange the invite list of the channel. */
1012 SILC_FSM_STATE(silc_client_command_invite)
1014 SilcClientCommandContext cmd = fsm_context;
1015 SilcClientConnection conn = cmd->conn;
1016 SilcClient client = conn->client;
1017 SilcClientEntry client_entry = NULL;
1018 SilcChannelEntry channel = NULL;
1019 SilcBuffer clidp, chidp, args = NULL;
1020 SilcPublicKey pubkey = NULL;
1021 SilcDList clients = NULL;
1022 char *nickname = NULL, *name;
1023 char *invite = NULL;
1024 unsigned char action[1];
1026 if (cmd->argc < 2) {
1027 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1028 "Usage: /INVITE <channel> [<nickname>[@server>]"
1029 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1030 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1034 if (cmd->argv[1][0] == '*') {
1035 if (!conn->current_channel) {
1036 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1040 channel = conn->current_channel;
1041 silc_client_ref_channel(client, conn, channel);
1043 name = cmd->argv[1];
1045 channel = silc_client_get_channel(conn->client, conn, name);
1047 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1052 /* Parse the typed nickname. */
1053 if (cmd->argc == 3) {
1054 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1055 silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
1057 /* Find client entry */
1058 clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1061 /* Resolve client information */
1062 SILC_FSM_CALL(silc_client_get_clients(
1063 client, conn, nickname, NULL,
1064 silc_client_command_resolve_continue,
1067 client_entry = silc_dlist_get(clients);
1069 if (cmd->argv[2][0] == '+')
1074 /* Check if it is public key file to be added to invite list */
1075 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1076 invite = cmd->argv[2];
1083 args = silc_buffer_alloc_size(2);
1084 silc_buffer_format(args,
1085 SILC_STR_UI_SHORT(1),
1088 chidp = silc_public_key_payload_encode(pubkey);
1089 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1090 silc_buffer_len(chidp), 2);
1091 silc_buffer_free(chidp);
1092 silc_pkcs_public_key_free(pubkey);
1094 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1098 /* Send the command */
1099 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1101 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1102 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1103 1, silc_buffer_datalen(chidp),
1104 2, silc_buffer_datalen(clidp),
1105 3, args ? action : NULL, args ? 1 : 0,
1106 4, silc_buffer_datalen(args));
1107 silc_buffer_free(clidp);
1109 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1110 1, silc_buffer_datalen(chidp),
1111 3, args ? action : NULL, args ? 1 : 0,
1112 4, silc_buffer_datalen(args));
1115 silc_buffer_free(chidp);
1116 silc_buffer_free(args);
1117 silc_free(nickname);
1118 silc_client_list_free(client, conn, clients);
1119 silc_client_unref_channel(client, conn, channel);
1121 /* Notify application */
1122 COMMAND(SILC_STATUS_OK);
1124 /** Wait for command reply */
1125 silc_fsm_next(fsm, silc_client_command_reply_wait);
1126 return SILC_FSM_CONTINUE;
1129 silc_free(nickname);
1130 return SILC_FSM_FINISH;
1133 /********************************** QUIT ************************************/
1135 /* Close the connection */
1137 SILC_FSM_STATE(silc_client_command_quit_final)
1139 SilcClientCommandContext cmd = fsm_context;
1140 SilcClientConnection conn = cmd->conn;
1142 SILC_LOG_DEBUG(("Quitting"));
1144 /* Notify application */
1145 COMMAND(SILC_STATUS_OK);
1147 /* Signal to close connection */
1148 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1149 if (!conn->internal->disconnected) {
1150 conn->internal->disconnected = TRUE;
1151 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1154 return SILC_FSM_FINISH;
1157 /* Command QUIT. Closes connection with current server. */
1159 SILC_FSM_STATE(silc_client_command_quit)
1161 SilcClientCommandContext cmd = fsm_context;
1162 SilcClientConnection conn = cmd->conn;
1165 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1166 1, cmd->argv[1], cmd->argv_lens[1]);
1168 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1170 /* Sleep for a while */
1173 /* We close the connection with a little timeout */
1174 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1175 return SILC_FSM_WAIT;
1178 /********************************** KILL ************************************/
1180 /* Command KILL. Router operator can use this command to remove an client
1181 fromthe SILC Network. */
1183 SILC_FSM_STATE(silc_client_command_kill)
1185 SilcClientCommandContext cmd = fsm_context;
1186 SilcClientConnection conn = cmd->conn;
1187 SilcClient client = conn->client;
1188 SilcBuffer idp, auth = NULL;
1189 SilcClientEntry target;
1191 char *nickname = NULL, *comment = NULL;
1193 if (cmd->argc < 2) {
1194 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1195 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1196 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1197 return SILC_FSM_FINISH;
1200 /* Parse the typed nickname. */
1201 if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1202 return SILC_FSM_FINISH;
1204 /* Get the target client */
1205 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1207 /* Resolve client information */
1208 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1209 silc_client_command_resolve_continue,
1212 target = silc_dlist_get(clients);
1214 if (cmd->argc >= 3) {
1215 if (strcasecmp(cmd->argv[2], "-pubkey"))
1216 comment = cmd->argv[2];
1218 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1219 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1220 /* Encode the public key authentication payload */
1221 auth = silc_auth_public_key_auth_generate(conn->public_key,
1224 conn->internal->sha1hash,
1225 &target->id, SILC_ID_CLIENT);
1229 /* Send the KILL command to the server */
1230 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1231 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1232 1, silc_buffer_datalen(idp),
1233 2, comment, comment ? strlen(comment) : 0,
1234 3, silc_buffer_datalen(auth));
1235 silc_buffer_free(idp);
1236 silc_buffer_free(auth);
1237 silc_free(nickname);
1238 silc_client_list_free(client, conn, clients);
1240 /* Notify application */
1241 COMMAND(SILC_STATUS_OK);
1243 /** Wait for command reply */
1244 silc_fsm_next(fsm, silc_client_command_reply_wait);
1245 return SILC_FSM_CONTINUE;
1248 /********************************** INFO ************************************/
1250 /* Command INFO. Request information about specific server. If specific
1251 server is not provided the current server is used. */
1253 SILC_FSM_STATE(silc_client_command_info)
1255 SilcClientCommandContext cmd = fsm_context;
1256 SilcClientConnection conn = cmd->conn;
1258 /* Send the command */
1260 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1261 1, cmd->argv[1], cmd->argv_lens[1]);
1263 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1265 /* Notify application */
1266 COMMAND(SILC_STATUS_OK);
1268 /** Wait for command reply */
1269 silc_fsm_next(fsm, silc_client_command_reply_wait);
1270 return SILC_FSM_CONTINUE;
1273 /********************************** STATS ***********************************/
1275 /* Command STATS. Shows server and network statistics. */
1277 SILC_FSM_STATE(silc_client_command_stats)
1279 SilcClientCommandContext cmd = fsm_context;
1280 SilcClientConnection conn = cmd->conn;
1282 /* Send the command */
1283 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1284 1, silc_buffer_datalen(conn->internal->
1287 /* Notify application */
1288 COMMAND(SILC_STATUS_OK);
1290 /** Wait for command reply */
1291 silc_fsm_next(fsm, silc_client_command_reply_wait);
1292 return SILC_FSM_CONTINUE;
1295 /********************************** PING ************************************/
1297 /* Command PING. Sends ping to server. */
1299 SILC_FSM_STATE(silc_client_command_ping)
1301 SilcClientCommandContext cmd = fsm_context;
1302 SilcClientConnection conn = cmd->conn;
1304 if (cmd->argc < 2) {
1305 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1306 return SILC_FSM_FINISH;
1309 /* Send the command */
1310 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1311 1, silc_buffer_datalen(conn->internal->
1314 /* Save ping time */
1315 cmd->context = SILC_64_TO_PTR(silc_time());
1317 /* Notify application */
1318 COMMAND(SILC_STATUS_OK);
1320 /** Wait for command reply */
1321 silc_fsm_next(fsm, silc_client_command_reply_wait);
1322 return SILC_FSM_CONTINUE;
1325 /********************************** JOIN ************************************/
1327 /* Command JOIN. Joins to a channel. */
1329 SILC_FSM_STATE(silc_client_command_join)
1331 SilcClientCommandContext cmd2, cmd = fsm_context;
1332 SilcClientConnection conn = cmd->conn;
1333 SilcClient client = conn->client;
1334 SilcChannelEntry channel = NULL;
1335 SilcBuffer auth = NULL, cauth = NULL;
1336 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1337 int i, passphrase_len = 0;
1339 if (cmd->argc < 2) {
1340 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1344 /* See if we have joined to the requested channel already */
1345 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1346 if (channel && silc_client_on_channel(channel, conn->local_entry))
1349 /* If NICK command is active, wait for it to finish before sending JOIN.
1350 To avoid problems locally with changing IDs while joining, we do this. */
1351 silc_mutex_lock(conn->internal->lock);
1352 silc_list_start(conn->internal->pending_commands);
1353 while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1354 if (cmd2->cmd == SILC_COMMAND_NICK) {
1355 silc_mutex_unlock(conn->internal->lock);
1356 silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1357 return SILC_FSM_WAIT;
1360 silc_mutex_unlock(conn->internal->lock);
1362 if (cmd->argv_lens[1] > 256)
1363 cmd->argv_lens[1] = 256;
1365 name = cmd->argv[1];
1367 for (i = 2; i < cmd->argc; i++) {
1368 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1369 cipher = cmd->argv[++i];
1370 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1371 hmac = cmd->argv[++i];
1372 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1373 auth = silc_auth_public_key_auth_generate(conn->public_key,
1376 conn->internal->sha1hash,
1379 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1380 SilcPublicKey pubkey = conn->public_key;
1381 SilcPrivateKey privkey = conn->private_key;
1382 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1385 if (cmd->argc >= i + 3) {
1387 if (cmd->argc >= i + 4) {
1388 pass = cmd->argv[i + 3];
1391 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1392 &pubkey, &privkey)) {
1393 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1394 "Could not load key pair, check your arguments");
1395 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1401 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1402 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1404 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1405 memcpy(pubdata, pkhash, 20);
1406 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1408 conn->internal->sha1hash,
1411 memset(pubdata, 0, 128);
1414 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1415 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1416 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1417 cmd->argv_lens[i], 0);
1418 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1419 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1420 0, pu8, passphrase_len);
1423 passphrase = strdup(cmd->argv[i]);
1424 passphrase_len = cmd->argv_lens[i];
1429 /* Send JOIN command to the server */
1430 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1431 1, name, strlen(name),
1432 2, silc_buffer_datalen(conn->internal->
1434 3, passphrase, passphrase_len,
1435 4, cipher, cipher ? strlen(cipher) : 0,
1436 5, hmac, hmac ? strlen(hmac) : 0,
1437 6, silc_buffer_datalen(auth),
1438 7, silc_buffer_datalen(cauth));
1440 silc_buffer_free(auth);
1441 silc_buffer_free(cauth);
1443 memset(passphrase, 0, strlen(passphrase));
1444 silc_free(passphrase);
1445 silc_client_unref_channel(client, conn, channel);
1447 /* Notify application */
1448 COMMAND(SILC_STATUS_OK);
1450 /** Wait for command reply */
1451 silc_fsm_next(fsm, silc_client_command_reply_wait);
1452 return SILC_FSM_CONTINUE;
1455 silc_client_unref_channel(client, conn, channel);
1456 return SILC_FSM_FINISH;
1459 /********************************** MOTD ************************************/
1461 /* MOTD command. Requests motd from server. */
1463 SILC_FSM_STATE(silc_client_command_motd)
1465 SilcClientCommandContext cmd = fsm_context;
1466 SilcClientConnection conn = cmd->conn;
1468 if (cmd->argc < 1 || cmd->argc > 2) {
1469 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1470 "Usage: /MOTD [<server>]");
1471 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1472 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1473 return SILC_FSM_FINISH;
1476 /* Send the command */
1478 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1479 1, conn->remote_host,
1480 strlen(conn->remote_host));
1482 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1483 1, cmd->argv[1], cmd->argv_lens[1]);
1485 /* Notify application */
1486 COMMAND(SILC_STATUS_OK);
1488 /** Wait for command reply */
1489 silc_fsm_next(fsm, silc_client_command_reply_wait);
1490 return SILC_FSM_CONTINUE;
1493 /********************************** UMODE ***********************************/
1495 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1496 modes as client cannot set itself server/router operator privileges. */
1498 SILC_FSM_STATE(silc_client_command_umode)
1500 SilcClientCommandContext cmd = fsm_context;
1501 SilcClientConnection conn = cmd->conn;
1502 unsigned char *cp, modebuf[4];
1503 SilcUInt32 mode, add, len;
1506 if (cmd->argc < 2) {
1507 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1508 "Usage: /UMODE +|-<modes>");
1509 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1510 return SILC_FSM_FINISH;
1513 mode = conn->local_entry->mode;
1515 /* Are we adding or removing mode */
1516 if (cmd->argv[1][0] == '-')
1522 cp = cmd->argv[1] + 1;
1524 for (i = 0; i < len; i++) {
1529 mode |= SILC_UMODE_SERVER_OPERATOR;
1530 mode |= SILC_UMODE_ROUTER_OPERATOR;
1531 mode |= SILC_UMODE_GONE;
1532 mode |= SILC_UMODE_INDISPOSED;
1533 mode |= SILC_UMODE_BUSY;
1534 mode |= SILC_UMODE_PAGE;
1535 mode |= SILC_UMODE_HYPER;
1536 mode |= SILC_UMODE_ROBOT;
1537 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1538 mode |= SILC_UMODE_REJECT_WATCHING;
1540 mode = SILC_UMODE_NONE;
1545 mode |= SILC_UMODE_SERVER_OPERATOR;
1547 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1551 mode |= SILC_UMODE_ROUTER_OPERATOR;
1553 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1557 mode |= SILC_UMODE_GONE;
1559 mode &= ~SILC_UMODE_GONE;
1563 mode |= SILC_UMODE_INDISPOSED;
1565 mode &= ~SILC_UMODE_INDISPOSED;
1569 mode |= SILC_UMODE_BUSY;
1571 mode &= ~SILC_UMODE_BUSY;
1575 mode |= SILC_UMODE_PAGE;
1577 mode &= ~SILC_UMODE_PAGE;
1581 mode |= SILC_UMODE_HYPER;
1583 mode &= ~SILC_UMODE_HYPER;
1587 mode |= SILC_UMODE_ROBOT;
1589 mode &= ~SILC_UMODE_ROBOT;
1593 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1595 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1599 mode |= SILC_UMODE_REJECT_WATCHING;
1601 mode &= ~SILC_UMODE_REJECT_WATCHING;
1605 mode |= SILC_UMODE_BLOCK_INVITE;
1607 mode &= ~SILC_UMODE_BLOCK_INVITE;
1610 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1611 return SILC_FSM_FINISH;
1616 SILC_PUT32_MSB(mode, modebuf);
1618 /* Send the command */
1619 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1620 1, silc_buffer_datalen(conn->internal->
1622 2, modebuf, sizeof(modebuf));
1624 /* Notify application */
1625 COMMAND(SILC_STATUS_OK);
1627 /** Wait for command reply */
1628 silc_fsm_next(fsm, silc_client_command_reply_wait);
1629 return SILC_FSM_CONTINUE;
1632 /********************************** CMODE ***********************************/
1634 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1635 can be set several at once. Those modes that require argument must be set
1636 separately (unless set with modes that does not require arguments). */
1638 SILC_FSM_STATE(silc_client_command_cmode)
1640 SilcClientCommandContext cmd = fsm_context;
1641 SilcClientConnection conn = cmd->conn;
1642 SilcClient client = conn->client;
1643 SilcChannelEntry channel = NULL;
1644 SilcBuffer chidp, auth = NULL, pk = NULL;
1645 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1646 SilcUInt32 mode, add, type, len, arg_len = 0;
1649 if (cmd->argc < 3) {
1650 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1651 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1652 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1656 if (cmd->argv[1][0] == '*') {
1657 if (!conn->current_channel) {
1658 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1662 channel = conn->current_channel;
1663 silc_client_ref_channel(client, conn, channel);
1665 name = cmd->argv[1];
1667 channel = silc_client_get_channel(conn->client, conn, name);
1669 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1674 mode = channel->mode;
1676 /* Are we adding or removing mode */
1677 if (cmd->argv[2][0] == '-')
1682 /* Argument type to be sent to server */
1686 cp = cmd->argv[2] + 1;
1688 for (i = 0; i < len; i++) {
1692 mode |= SILC_CHANNEL_MODE_PRIVATE;
1694 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1698 mode |= SILC_CHANNEL_MODE_SECRET;
1700 mode &= ~SILC_CHANNEL_MODE_SECRET;
1704 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1706 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1710 mode |= SILC_CHANNEL_MODE_INVITE;
1712 mode &= ~SILC_CHANNEL_MODE_INVITE;
1716 mode |= SILC_CHANNEL_MODE_TOPIC;
1718 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1722 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1724 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1728 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1730 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1735 mode |= SILC_CHANNEL_MODE_ULIMIT;
1737 if (cmd->argc < 4) {
1738 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1739 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1740 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1743 ll = atoi(cmd->argv[3]);
1744 SILC_PUT32_MSB(ll, tmp);
1748 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1753 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1755 if (cmd->argc < 4) {
1756 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1757 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1758 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1762 arg_len = cmd->argv_lens[3];
1764 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1769 mode |= SILC_CHANNEL_MODE_CIPHER;
1771 if (cmd->argc < 4) {
1772 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1773 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1774 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1778 arg_len = cmd->argv_lens[3];
1780 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1785 mode |= SILC_CHANNEL_MODE_HMAC;
1787 if (cmd->argc < 4) {
1788 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1789 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1790 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1794 arg_len = cmd->argv_lens[3];
1796 mode &= ~SILC_CHANNEL_MODE_HMAC;
1801 SilcPublicKey pubkey = conn->public_key;
1802 SilcPrivateKey privkey = conn->private_key;
1804 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1807 if (cmd->argc >= 5) {
1810 pass = cmd->argv[5];
1811 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1812 &pubkey, &privkey)) {
1813 SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1814 "Could not load key pair, check your arguments");
1815 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1820 pk = silc_public_key_payload_encode(pubkey);
1821 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1823 conn->internal->sha1hash,
1826 arg = silc_buffer_data(auth);
1827 arg_len = silc_buffer_len(auth);
1829 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1835 SilcBool chadd = FALSE;
1836 SilcPublicKey chpk = NULL;
1838 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1841 if (cmd->argc == 3) {
1842 /* Send empty command to receive the public key list. */
1843 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1844 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1846 1, silc_buffer_datalen(chidp));
1847 silc_buffer_free(chidp);
1848 silc_client_unref_channel(client, conn, channel);
1850 /* Notify application */
1851 COMMAND(SILC_STATUS_OK);
1853 /** Wait for command reply */
1854 silc_fsm_next(fsm, silc_client_command_reply_wait);
1855 return SILC_FSM_CONTINUE;
1858 if (cmd->argc >= 4) {
1859 auth = silc_buffer_alloc_size(2);
1860 silc_buffer_format(auth,
1861 SILC_STR_UI_SHORT(cmd->argc - 3),
1865 for (k = 3; k < cmd->argc; k++) {
1866 if (cmd->argv[k][0] == '+')
1868 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1869 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1870 "Could not load public key %s, check the filename",
1872 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1873 silc_buffer_free(auth);
1878 pk = silc_public_key_payload_encode(chpk);
1879 auth = silc_argument_payload_encode_one(auth,
1880 silc_buffer_datalen(pk),
1881 chadd ? 0x00 : 0x01);
1882 silc_pkcs_public_key_free(chpk);
1883 silc_buffer_free(pk);
1888 arg = silc_buffer_data(auth);
1889 arg_len = silc_buffer_len(auth);
1891 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1895 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1901 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1902 SILC_PUT32_MSB(mode, modebuf);
1904 /* Send the command. We support sending only one mode at once that
1905 requires an argument. */
1907 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1908 1, silc_buffer_datalen(chidp),
1909 2, modebuf, sizeof(modebuf),
1911 8, silc_buffer_datalen(pk));
1913 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1914 1, silc_buffer_datalen(chidp),
1915 2, modebuf, sizeof(modebuf));
1918 silc_buffer_free(chidp);
1919 silc_buffer_free(auth);
1920 silc_buffer_free(pk);
1921 silc_client_unref_channel(client, conn, channel);
1923 /* Notify application */
1924 COMMAND(SILC_STATUS_OK);
1926 /** Wait for command reply */
1927 silc_fsm_next(fsm, silc_client_command_reply_wait);
1928 return SILC_FSM_CONTINUE;
1931 silc_client_unref_channel(client, conn, channel);
1932 return SILC_FSM_FINISH;
1935 /********************************* CUMODE ***********************************/
1937 /* CUMODE command. Changes client's mode on a channel. */
1939 SILC_FSM_STATE(silc_client_command_cumode)
1941 SilcClientCommandContext cmd = fsm_context;
1942 SilcClientConnection conn = cmd->conn;
1943 SilcClient client = conn->client;
1944 SilcChannelEntry channel = NULL;
1945 SilcChannelUser chu;
1946 SilcClientEntry client_entry;
1947 SilcBuffer clidp, chidp, auth = NULL;
1948 SilcDList clients = NULL;
1949 unsigned char *name, *cp, modebuf[4];
1950 SilcUInt32 mode = 0, add, len;
1951 char *nickname = NULL;
1954 if (cmd->argc < 4) {
1955 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1956 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1957 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1961 if (cmd->argv[1][0] == '*') {
1962 if (!conn->current_channel) {
1963 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1967 channel = conn->current_channel;
1968 silc_client_ref_channel(client, conn, channel);
1970 name = cmd->argv[1];
1972 channel = silc_client_get_channel(conn->client, conn, name);
1974 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1979 /* Parse the typed nickname. */
1980 silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
1982 /* Find client entry */
1983 clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
1985 /* Resolve client information */
1986 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1987 silc_client_command_resolve_continue,
1990 client_entry = silc_dlist_get(clients);
1992 /* Get the current mode */
1993 chu = silc_client_on_channel(channel, client_entry);
1997 /* Are we adding or removing mode */
1998 if (cmd->argv[2][0] == '-')
2004 cp = cmd->argv[2] + 1;
2006 for (i = 0; i < len; i++) {
2010 mode |= SILC_CHANNEL_UMODE_CHANFO;
2011 mode |= SILC_CHANNEL_UMODE_CHANOP;
2012 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2013 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2014 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2016 mode = SILC_CHANNEL_UMODE_NONE;
2021 SilcPublicKey pubkey = conn->public_key;
2022 SilcPrivateKey privkey = conn->private_key;
2024 if (cmd->argc >= 6) {
2027 pass = cmd->argv[6];
2028 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2029 &pubkey, &privkey)) {
2030 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2031 "Could not load key pair, check your arguments");
2032 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2037 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
2039 conn->internal->sha1hash,
2042 mode |= SILC_CHANNEL_UMODE_CHANFO;
2044 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2049 mode |= SILC_CHANNEL_UMODE_CHANOP;
2051 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2055 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2057 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2061 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2063 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2067 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2069 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2073 mode |= SILC_CHANNEL_UMODE_QUIET;
2075 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2078 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2084 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2085 SILC_PUT32_MSB(mode, modebuf);
2086 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2088 /* Send the command packet. We support sending only one mode at once
2089 that requires an argument. */
2090 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2091 1, silc_buffer_datalen(chidp),
2093 3, silc_buffer_datalen(clidp),
2094 4, silc_buffer_datalen(auth));
2096 silc_buffer_free(chidp);
2097 silc_buffer_free(clidp);
2099 silc_buffer_free(auth);
2100 silc_free(nickname);
2101 silc_client_list_free(client, conn, clients);
2102 silc_client_unref_channel(client, conn, channel);
2104 /* Notify application */
2105 COMMAND(SILC_STATUS_OK);
2107 /** Wait for command reply */
2108 silc_fsm_next(fsm, silc_client_command_reply_wait);
2109 return SILC_FSM_CONTINUE;
2112 silc_client_unref_channel(client, conn, channel);
2113 silc_client_list_free(client, conn, clients);
2114 silc_free(nickname);
2115 return SILC_FSM_FINISH;
2118 /********************************** KICK ************************************/
2120 /* KICK command. Kicks a client out of channel. */
2122 SILC_FSM_STATE(silc_client_command_kick)
2124 SilcClientCommandContext cmd = fsm_context;
2125 SilcClientConnection conn = cmd->conn;
2126 SilcClient client = conn->client;
2127 SilcChannelEntry channel = NULL;
2128 SilcBuffer idp, idp2;
2129 SilcClientEntry target;
2130 SilcDList clients = NULL;
2131 char *name, tmp[512];
2133 if (cmd->argc < 3) {
2134 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2135 "Usage: /KICK <channel> <nickname> [<comment>]");
2136 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2140 if (cmd->argv[1][0] == '*') {
2141 if (!conn->current_channel) {
2142 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2146 if (client->internal->params->full_channel_names)
2147 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2149 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2150 conn->current_channel->channel_name,
2151 conn->current_channel->server[0] ? "@" : "",
2152 conn->current_channel->server);
2155 name = cmd->argv[1];
2158 if (!conn->current_channel) {
2159 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2163 /* Get the Channel ID of the channel */
2164 channel = silc_client_get_channel(conn->client, conn, name);
2166 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2170 /* Get the target client */
2171 clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2173 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2174 "No such client: %s", cmd->argv[2]);
2175 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2178 target = silc_dlist_get(clients);
2180 /* Send KICK command to the server */
2181 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2182 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2184 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2185 1, silc_buffer_datalen(idp),
2186 2, silc_buffer_datalen(idp2));
2188 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2189 1, silc_buffer_datalen(idp),
2190 2, silc_buffer_datalen(idp2),
2191 3, cmd->argv[3], strlen(cmd->argv[3]));
2193 silc_buffer_free(idp);
2194 silc_buffer_free(idp2);
2195 silc_client_list_free(client, conn, clients);
2196 silc_client_unref_channel(client, conn, channel);
2198 /* Notify application */
2199 COMMAND(SILC_STATUS_OK);
2201 /** Wait for command reply */
2202 silc_fsm_next(fsm, silc_client_command_reply_wait);
2203 return SILC_FSM_CONTINUE;
2206 silc_client_unref_channel(client, conn, channel);
2207 return SILC_FSM_FINISH;
2210 /***************************** OPER & SILCOPER ******************************/
2213 unsigned char *passphrase;
2214 SilcUInt32 passphrase_len;
2215 } *SilcClientCommandOper;
2217 /* Ask passphrase callback */
2219 static void silc_client_command_oper_cb(const unsigned char *data,
2220 SilcUInt32 data_len, void *context)
2222 SilcClientCommandContext cmd = context;
2223 SilcClientCommandOper oper = cmd->context;
2225 if (data && data_len)
2226 oper->passphrase = silc_memdup(data, data_len);
2227 oper->passphrase_len = data_len;
2230 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2233 /* Send OPER/SILCOPER command */
2235 SILC_FSM_STATE(silc_client_command_oper_send)
2237 SilcClientCommandContext cmd = fsm_context;
2238 SilcClientConnection conn = cmd->conn;
2239 SilcClientCommandOper oper = cmd->context;
2242 if (!oper || !oper->passphrase) {
2243 /* Encode the public key authentication payload */
2244 auth = silc_auth_public_key_auth_generate(conn->public_key,
2247 conn->internal->hash,
2251 /* Encode the password authentication payload */
2252 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2253 oper->passphrase, oper->passphrase_len);
2256 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2257 1, cmd->argv[1], strlen(cmd->argv[1]),
2258 2, silc_buffer_datalen(auth));
2260 silc_buffer_clear(auth);
2261 silc_buffer_free(auth);
2263 silc_free(oper->passphrase);
2267 /* Notify application */
2268 COMMAND(SILC_STATUS_OK);
2270 /** Wait for command reply */
2271 silc_fsm_next(fsm, silc_client_command_reply_wait);
2272 return SILC_FSM_CONTINUE;
2275 /* OPER command. Used to obtain server operator privileges. */
2277 SILC_FSM_STATE(silc_client_command_oper)
2279 SilcClientCommandContext cmd = fsm_context;
2280 SilcClientConnection conn = cmd->conn;
2281 SilcClientCommandOper oper;
2283 if (cmd->argc < 2) {
2284 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2285 "Usage: /OPER <username> [-pubkey]");
2286 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2287 return SILC_FSM_FINISH;
2290 silc_fsm_next(fsm, silc_client_command_oper_send);
2292 /* Get passphrase */
2293 if (cmd->argc < 3) {
2294 oper = silc_calloc(1, sizeof(*oper));
2296 return SILC_FSM_FINISH;
2297 cmd->context = oper;
2298 SILC_FSM_CALL(conn->client->internal->
2299 ops->ask_passphrase(conn->client, conn,
2300 silc_client_command_oper_cb, cmd));
2303 return SILC_FSM_CONTINUE;
2306 /* SILCOPER command. Used to obtain router operator privileges. */
2308 SILC_FSM_STATE(silc_client_command_silcoper)
2310 SilcClientCommandContext cmd = fsm_context;
2311 SilcClientConnection conn = cmd->conn;
2312 SilcClientCommandOper oper;
2314 if (cmd->argc < 2) {
2315 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2316 "Usage: /SILCOPER <username> [-pubkey]");
2317 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2318 return SILC_FSM_FINISH;
2321 silc_fsm_next(fsm, silc_client_command_oper_send);
2323 /* Get passphrase */
2324 if (cmd->argc < 3) {
2325 oper = silc_calloc(1, sizeof(*oper));
2327 return SILC_FSM_FINISH;
2328 cmd->context = oper;
2329 SILC_FSM_CALL(conn->client->internal->
2330 ops->ask_passphrase(conn->client, conn,
2331 silc_client_command_oper_cb, cmd));
2334 return SILC_FSM_CONTINUE;
2337 /*********************************** BAN ************************************/
2339 /* Command BAN. This is used to manage the ban list of the channel. */
2341 SILC_FSM_STATE(silc_client_command_ban)
2343 SilcClientCommandContext cmd = fsm_context;
2344 SilcClientConnection conn = cmd->conn;
2345 SilcClient client = conn->client;
2346 SilcChannelEntry channel;
2347 SilcBuffer chidp, args = NULL;
2348 char *name, *ban = NULL;
2349 unsigned char action[1];
2350 SilcPublicKey pubkey = NULL;
2352 if (cmd->argc < 2) {
2353 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2354 "Usage: /BAN <channel> "
2355 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2356 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2360 if (cmd->argv[1][0] == '*') {
2361 if (!conn->current_channel) {
2362 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2366 channel = conn->current_channel;
2367 silc_client_ref_channel(client, conn, channel);
2369 name = cmd->argv[1];
2371 channel = silc_client_get_channel(conn->client, conn, name);
2373 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2378 if (cmd->argc == 3) {
2379 if (cmd->argv[2][0] == '+')
2384 /* Check if it is public key file to be added to invite list */
2385 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2392 args = silc_buffer_alloc_size(2);
2393 silc_buffer_format(args,
2394 SILC_STR_UI_SHORT(1),
2397 chidp = silc_public_key_payload_encode(pubkey);
2398 args = silc_argument_payload_encode_one(args,
2399 silc_buffer_datalen(chidp), 2);
2400 silc_buffer_free(chidp);
2401 silc_pkcs_public_key_free(pubkey);
2403 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2407 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2409 /* Send the command */
2410 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2411 1, silc_buffer_datalen(chidp),
2412 2, args ? action : NULL, args ? 1 : 0,
2413 3, silc_buffer_datalen(args));
2415 silc_buffer_free(chidp);
2416 silc_buffer_free(args);
2417 silc_client_unref_channel(client, conn, channel);
2419 /* Notify application */
2420 COMMAND(SILC_STATUS_OK);
2422 /** Wait for command reply */
2423 silc_fsm_next(fsm, silc_client_command_reply_wait);
2424 return SILC_FSM_CONTINUE;
2427 return SILC_FSM_FINISH;
2430 /********************************* DETACH ***********************************/
2432 /* Command DETACH. This is used to detach from the server */
2434 SILC_FSM_STATE(silc_client_command_detach)
2436 SilcClientCommandContext cmd = fsm_context;
2437 SilcClientConnection conn = cmd->conn;
2439 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2441 /* Notify application */
2442 COMMAND(SILC_STATUS_OK);
2444 /** Wait for command reply */
2445 silc_fsm_next(fsm, silc_client_command_reply_wait);
2446 return SILC_FSM_CONTINUE;
2449 /********************************** WATCH ***********************************/
2451 /* Command WATCH. */
2453 SILC_FSM_STATE(silc_client_command_watch)
2455 SilcClientCommandContext cmd = fsm_context;
2456 SilcClientConnection conn = cmd->conn;
2457 SilcBuffer args = NULL;
2459 const char *pubkey = NULL;
2460 SilcBool pubkey_add = TRUE;
2462 if (cmd->argc < 3) {
2463 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2467 if (!strcasecmp(cmd->argv[1], "-add")) {
2469 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2471 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2473 pubkey = cmd->argv[2] + 1;
2474 if (cmd->argv[2][0] == '-')
2477 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2485 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2486 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2487 "Could not load public key %s, check the filename", pubkey);
2488 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2492 args = silc_buffer_alloc_size(2);
2493 silc_buffer_format(args,
2494 SILC_STR_UI_SHORT(1),
2496 buffer = silc_public_key_payload_encode(pk);
2497 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2498 pubkey_add ? 0x00 : 0x01);
2499 silc_buffer_free(buffer);
2500 silc_pkcs_public_key_free(pk);
2503 /* If watching by nickname, resolve all users with that nickname so that
2504 we get their information immediately. */
2506 silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2507 silc_client_command_resolve_dummy, NULL);
2509 /* Send the commmand */
2510 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2511 1, silc_buffer_datalen(conn->internal->
2513 type, pubkey ? args->data : cmd->argv[2],
2514 pubkey ? silc_buffer_len(args) :
2517 silc_buffer_free(args);
2519 /* Notify application */
2520 COMMAND(SILC_STATUS_OK);
2522 /** Wait for command reply */
2523 silc_fsm_next(fsm, silc_client_command_reply_wait);
2524 return SILC_FSM_CONTINUE;
2527 return SILC_FSM_FINISH;
2530 /********************************** LEAVE ***********************************/
2532 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2534 SILC_FSM_STATE(silc_client_command_leave)
2536 SilcClientCommandContext cmd = fsm_context;
2537 SilcClientConnection conn = cmd->conn;
2538 SilcClient client = conn->client;
2539 SilcChannelEntry channel;
2541 char *name, tmp[512];
2543 if (cmd->argc != 2) {
2544 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2545 "Usage: /LEAVE <channel>");
2546 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2550 if (cmd->argv[1][0] == '*') {
2551 if (!conn->current_channel) {
2552 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2556 if (client->internal->params->full_channel_names)
2557 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2559 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2560 conn->current_channel->channel_name,
2561 conn->current_channel->server[0] ? "@" : "",
2562 conn->current_channel->server);
2565 name = cmd->argv[1];
2568 /* Get the channel entry */
2569 channel = silc_client_get_channel(conn->client, conn, name);
2571 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2575 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2577 /* Send LEAVE command to the server */
2578 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2579 1, silc_buffer_datalen(idp));
2581 silc_buffer_free(idp);
2583 /* Notify application */
2584 COMMAND(SILC_STATUS_OK);
2586 if (conn->current_channel == channel)
2587 conn->current_channel = NULL;
2589 silc_client_unref_channel(client, conn, channel);
2591 /** Wait for command reply */
2592 silc_fsm_next(fsm, silc_client_command_reply_wait);
2593 return SILC_FSM_CONTINUE;
2596 return SILC_FSM_FINISH;
2599 /********************************** USERS ***********************************/
2601 /* Command USERS. Requests the USERS of the clients joined on requested
2604 SILC_FSM_STATE(silc_client_command_users)
2606 SilcClientCommandContext cmd = fsm_context;
2607 SilcClientConnection conn = cmd->conn;
2608 char *name, tmp[512];
2610 if (cmd->argc != 2) {
2611 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2612 "Usage: /USERS <channel>");
2613 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2617 if (cmd->argv[1][0] == '*') {
2618 if (!conn->current_channel) {
2619 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2623 if (conn->client->internal->params->full_channel_names)
2624 silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2626 silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2627 conn->current_channel->channel_name,
2628 conn->current_channel->server[0] ? "@" : "",
2629 conn->current_channel->server);
2632 name = cmd->argv[1];
2635 /* Send USERS command to the server */
2636 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2637 2, name, strlen(name));
2639 /* Notify application */
2640 COMMAND(SILC_STATUS_OK);
2642 /** Wait for command reply */
2643 silc_fsm_next(fsm, silc_client_command_reply_wait);
2644 return SILC_FSM_CONTINUE;
2647 return SILC_FSM_FINISH;
2650 /********************************* GETKEY ***********************************/
2652 /* Command GETKEY. Used to fetch remote client's public key. */
2654 SILC_FSM_STATE(silc_client_command_getkey)
2656 SilcClientCommandContext cmd = fsm_context;
2657 SilcClientConnection conn = cmd->conn;
2658 SilcClient client = conn->client;
2659 SilcClientEntry client_entry;
2660 SilcServerEntry server_entry;
2664 if (cmd->argc < 2) {
2665 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2666 "Usage: /GETKEY <nickname or server name>");
2667 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2668 return SILC_FSM_FINISH;
2671 /* Find client entry */
2672 clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2674 /* Check whether user requested server */
2675 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2676 if (!server_entry) {
2677 if (cmd->resolved) {
2678 /* Resolving didn't find anything. We should never get here as
2679 errors are handled in the resolving callback. */
2680 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2681 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2682 return SILC_FSM_FINISH;
2685 /* No client or server exist with this name, query for both. */
2686 cmd->resolved = TRUE;
2687 SILC_FSM_CALL(silc_client_command_send(client, conn,
2688 SILC_COMMAND_IDENTIFY,
2689 silc_client_command_continue,
2692 strlen(cmd->argv[1]),
2694 strlen(cmd->argv[1])));
2697 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2698 silc_client_unref_server(client, conn, server_entry);
2700 client_entry = silc_dlist_get(clients);
2701 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2702 silc_client_list_free(client, conn, clients);
2705 /* Send the commmand */
2706 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2707 1, silc_buffer_datalen(idp));
2709 silc_buffer_free(idp);
2711 /* Notify application */
2712 COMMAND(SILC_STATUS_OK);
2714 /** Wait for command reply */
2715 silc_fsm_next(fsm, silc_client_command_reply_wait);
2716 return SILC_FSM_CONTINUE;
2719 /********************************* SERVICE **********************************/
2721 /* Command SERVICE. Negotiates service agreement with server. */
2722 /* XXX incomplete */
2724 SILC_FSM_STATE(silc_client_command_service)
2726 SilcClientCommandContext cmd = fsm_context;
2728 SilcClientConnection conn = cmd->conn;
2732 if (cmd->argc < 2) {
2733 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2734 "Usage: /SERVICE [<service name>] [-pubkey]");
2735 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2736 return SILC_FSM_FINISH;
2739 name = cmd->argv[1];
2741 /* Send SERVICE command to the server */
2742 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2743 ++conn->cmd_ident, 1,
2744 1, name, strlen(name));
2745 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2746 NULL, 0, NULL, NULL, buffer->data,
2748 silc_buffer_free(buffer);
2751 /* Notify application */
2752 COMMAND(SILC_STATUS_OK);
2754 /** Wait for command reply */
2755 silc_fsm_next(fsm, silc_client_command_reply_wait);
2756 return SILC_FSM_CONTINUE;
2759 /* Register all default commands provided by the client library for the
2762 void silc_client_commands_register(SilcClient client)
2764 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2767 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2768 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2769 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2770 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2771 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2772 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2773 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2774 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2775 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2776 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2777 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2778 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2779 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2780 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2781 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2782 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2783 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2784 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2785 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2786 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2787 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2788 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2789 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2790 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2791 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2792 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2793 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2796 /* Unregister all commands. */
2798 void silc_client_commands_unregister(SilcClient client)
2800 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2801 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2802 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2803 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2804 SILC_CLIENT_CMDU(list, LIST, "LIST");
2805 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2806 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2807 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2808 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2809 SILC_CLIENT_CMDU(info, INFO, "INFO");
2810 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2811 SILC_CLIENT_CMDU(ping, PING, "PING");
2812 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2813 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2814 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2815 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2816 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2817 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2818 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2819 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2820 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2821 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2822 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2823 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2824 SILC_CLIENT_CMDU(users, USERS, "USERS");
2825 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2826 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2829 /****************** Client Side Incoming Command Handling *******************/
2831 /* Reply to WHOIS command from server */
2833 static void silc_client_command_process_whois(SilcClient client,
2834 SilcClientConnection conn,
2835 SilcCommandPayload payload,
2836 SilcArgumentPayload args)
2841 SilcBuffer buffer, packet;
2843 SILC_LOG_DEBUG(("Received WHOIS command"));
2845 /* Try to take the Requested Attributes */
2846 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2850 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2854 /* Process requested attributes */
2855 buffer = silc_client_attributes_process(client, conn, attrs);
2857 silc_attribute_payload_list_free(attrs);
2861 /* Send the attributes back in COMMAND_REPLY packet */
2863 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2865 silc_command_get_ident(payload),
2866 1, 11, buffer->data,
2867 silc_buffer_len(buffer));
2869 silc_buffer_free(buffer);
2873 SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
2875 silc_packet_send(conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
2876 silc_buffer_datalen(packet));
2878 silc_buffer_free(packet);
2879 silc_buffer_free(buffer);
2882 /* Client is able to receive some command packets even though they are
2883 special case. Server may send WHOIS command to the client to retrieve
2884 Requested Attributes information for WHOIS query the server is
2885 processing. This function currently handles only the WHOIS command,
2886 but if in the future more commands may arrive then this can be made
2887 to support other commands too. */
2889 SILC_FSM_STATE(silc_client_command)
2891 SilcClientConnection conn = fsm_context;
2892 SilcClient client = conn->client;
2893 SilcPacket packet = state_context;
2894 SilcCommandPayload payload;
2895 SilcCommand command;
2896 SilcArgumentPayload args;
2898 /* Get command payload from packet */
2899 payload = silc_command_payload_parse(packet->buffer.data,
2900 silc_buffer_len(&packet->buffer));
2902 SILC_LOG_DEBUG(("Bad command packet"));
2903 return SILC_FSM_FINISH;
2907 args = silc_command_get_args(payload);
2909 /* Get the command */
2910 command = silc_command_get(payload);
2913 case SILC_COMMAND_WHOIS:
2914 /* Ignore everything if requested by application */
2915 if (conn->internal->params.ignore_requested_attributes)
2918 silc_client_command_process_whois(client, conn, payload, args);
2925 silc_command_payload_free(payload);
2926 return SILC_FSM_FINISH;