5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 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);
123 silc_dlist_uninit(clients);
125 /* Continue with the command */
126 SILC_FSM_CALL_CONTINUE(&cmd->thread);
129 /* Register command to client */
132 silc_client_command_register(SilcClient client,
135 SilcFSMStateCallback command_func,
136 SilcFSMStateCallback command_reply_func,
139 SilcClientCommand cmd;
141 cmd = silc_calloc(1, sizeof(*cmd));
145 cmd->command = command_func;
146 cmd->reply = command_reply_func;
147 cmd->max_args = max_args;
148 cmd->name = name ? strdup(name) : NULL;
154 silc_list_add(client->internal->commands, cmd);
159 /* Unregister command from client */
162 silc_client_command_unregister(SilcClient client,
164 SilcFSMStateCallback command_func,
165 SilcFSMStateCallback command_reply_func)
167 SilcClientCommand cmd;
169 silc_list_start(client->internal->commands);
170 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
171 if (cmd->cmd == command && cmd->command == command_func &&
172 cmd->reply == command_reply_func) {
173 silc_list_del(client->internal->commands, cmd);
174 silc_free(cmd->name);
183 /* Finds and returns a pointer to the command list. Return NULL if the
184 command is not found. */
186 static SilcClientCommand silc_client_command_find(SilcClient client,
189 SilcClientCommand cmd;
191 silc_list_start(client->internal->commands);
192 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
193 if (cmd->name && !strcasecmp(cmd->name, name))
200 /* Command thread destructor */
202 static void silc_client_command_destructor(SilcFSMThread thread,
204 void *destructor_context)
206 silc_client_command_free(fsm_context);
209 /* Add a command pending a command reply. Used internally by the library. */
212 silc_client_command_add_pending(SilcClientConnection conn,
213 SilcClientCommandContext cmd,
214 SilcClientCommandReply reply,
217 SilcClientCommandReplyCallback cb;
219 silc_mutex_lock(conn->internal->lock);
221 /* Add pending callback, if defined */
223 cb = silc_calloc(1, sizeof(*cb));
225 silc_mutex_unlock(conn->internal->lock);
229 cb->context = context;
230 silc_list_add(cmd->reply_callbacks, cb);
233 /* Add pending reply */
234 silc_list_add(conn->internal->pending_commands, cmd);
236 silc_mutex_unlock(conn->internal->lock);
241 /* Generic function to send any command. The arguments must be sent already
242 encoded into correct format and in correct order. Arguments come from
243 variable argument list pointer. */
245 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
246 SilcClientConnection conn,
247 SilcClientCommandContext cmd,
249 SilcClientCommandReply reply,
251 SilcUInt32 argc, va_list ap)
255 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
258 cmd->cmd_ident = silc_client_cmd_ident(conn);
260 /* Encode command payload */
261 packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
265 /* Send the command */
266 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
267 silc_buffer_datalen(packet))) {
268 silc_buffer_free(packet);
272 /* Add the command pending command reply */
273 silc_client_command_add_pending(conn, cmd, reply, reply_context);
275 silc_buffer_free(packet);
277 return cmd->cmd_ident;
280 /* Generic function to send any command. The arguments must be sent already
281 encoded into correct format and in correct order. Arguments come from
285 silc_client_command_send_arg_array(SilcClient client,
286 SilcClientConnection conn,
287 SilcClientCommandContext cmd,
289 SilcClientCommandReply reply,
292 unsigned char **argv,
293 SilcUInt32 *argv_lens,
294 SilcUInt32 *argv_types)
298 SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
301 cmd->cmd_ident = silc_client_cmd_ident(conn);
303 /* Encode command payload */
304 packet = silc_command_payload_encode(command, argc, argv, argv_lens,
305 argv_types, cmd->cmd_ident);
309 /* Send the command */
310 if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
311 silc_buffer_datalen(packet))) {
312 silc_buffer_free(packet);
316 /* Add the command pending command reply */
317 silc_client_command_add_pending(conn, cmd, reply, reply_context);
319 silc_buffer_free(packet);
321 return cmd->cmd_ident;
324 /* Generic function to send any command. The arguments must be sent already
325 encoded into correct format and in correct order. This is used internally
328 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
329 SilcClientCommandContext cmd,
331 SilcClientCommandReply reply,
333 SilcUInt32 argc, ...)
336 SilcUInt16 cmd_ident;
339 cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
340 reply, reply_context, argc, ap);
346 /****************************** Command API *********************************/
348 /* Free command context and its internals */
350 void silc_client_command_free(SilcClientCommandContext cmd)
352 SilcClientCommandReplyCallback cb;
355 for (i = 0; i < cmd->argc; i++)
356 silc_free(cmd->argv[i]);
357 silc_free(cmd->argv);
358 silc_free(cmd->argv_lens);
359 silc_free(cmd->argv_types);
361 silc_list_start(cmd->reply_callbacks);
362 while ((cb = silc_list_get(cmd->reply_callbacks)))
368 /* Executes a command */
370 SilcUInt16 silc_client_command_call(SilcClient client,
371 SilcClientConnection conn,
372 const char *command_line, ...)
376 unsigned char **argv = NULL;
377 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
378 SilcClientCommand command;
379 SilcClientCommandContext cmd;
383 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_ERROR,
384 "You are not connected to a server, please connect to server");
388 /* Parse arguments */
389 va_start(va, command_line);
393 /* Get command name */
394 command_name = silc_memdup(command_line, strcspn(command_line, " "));
398 /* Find command by name */
399 command = silc_client_command_find(client, command_name);
401 silc_free(command_name);
405 /* Parse command line */
406 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
407 &argv_types, &argc, command->max_args);
409 silc_free(command_name);
411 arg = va_arg(va, char *);
415 /* Find command by name */
416 command = silc_client_command_find(client, arg);
421 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
422 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
423 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
424 if (!argv || !argv_lens || !argv_types)
426 argv[argc] = silc_memdup(arg, strlen(arg));
429 argv_lens[argc] = strlen(arg);
430 argv_types[argc] = argc;
432 arg = va_arg(va, char *);
437 /* Allocate command context */
438 cmd = silc_calloc(1, sizeof(*cmd));
442 cmd->cmd = command->cmd;
445 cmd->argv_lens = argv_lens;
446 cmd->argv_types = argv_types;
447 cmd->cmd_ident = silc_client_cmd_ident(conn);
450 silc_list_init(cmd->reply_callbacks,
451 struct SilcClientCommandReplyCallbackStruct, next);
454 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
455 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
456 silc_client_command_destructor, NULL, FALSE);
457 silc_fsm_start_sync(&cmd->thread, command->command);
459 return cmd->cmd_ident;
462 /* Generic function to send any command. The arguments must be sent already
463 encoded into correct format and in correct order. */
465 SilcUInt16 silc_client_command_send(SilcClient client,
466 SilcClientConnection conn,
468 SilcClientCommandReply reply,
470 SilcUInt32 argc, ...)
472 SilcClientCommandContext cmd;
478 /* Allocate command context */
479 cmd = silc_calloc(1, sizeof(*cmd));
484 silc_list_init(cmd->reply_callbacks,
485 struct SilcClientCommandReplyCallbackStruct, next);
487 /* Send the command */
490 silc_client_command_send_vap(client, conn, cmd, command, reply,
491 reply_context, argc, ap);
494 if (!cmd->cmd_ident) {
495 silc_client_command_free(cmd);
499 /*** Wait for command reply */
500 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
501 silc_client_command_destructor, NULL, FALSE);
502 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
504 return cmd->cmd_ident;
507 /* Generic function to send any command. The arguments must be sent already
508 encoded into correct format and in correct order. Arguments come from
511 SilcUInt16 silc_client_command_send_argv(SilcClient client,
512 SilcClientConnection conn,
514 SilcClientCommandReply reply,
517 unsigned char **argv,
518 SilcUInt32 *argv_lens,
519 SilcUInt32 *argv_types)
521 SilcClientCommandContext cmd;
526 /* Allocate command context */
527 cmd = silc_calloc(1, sizeof(*cmd));
533 /* Send the command */
535 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
536 reply_context, argc, argv, argv_lens,
538 if (!cmd->cmd_ident) {
539 silc_client_command_free(cmd);
543 /*** Wait for command reply */
544 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
545 silc_client_command_destructor, NULL, FALSE);
546 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
548 return cmd->cmd_ident;
551 /* Attach to a command and command identifier to receive command reply. */
553 SilcBool silc_client_command_pending(SilcClientConnection conn,
556 SilcClientCommandReply reply,
559 SilcClientCommandContext cmd;
560 SilcClientCommandReplyCallback cb;
565 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
567 silc_mutex_lock(conn->internal->lock);
569 /* Find the pending command */
570 silc_list_start(conn->internal->pending_commands);
571 while ((cmd = silc_list_get(conn->internal->pending_commands)))
572 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
573 && cmd->cmd_ident == ident) {
575 /* Add the callback */
576 cb = silc_calloc(1, sizeof(*cb));
580 cb->context = context;
581 silc_list_add(cmd->reply_callbacks, cb);
584 silc_mutex_unlock(conn->internal->lock);
589 /******************************** WHOIS *************************************/
591 /* Command WHOIS. This command is used to query information about
594 SILC_FSM_STATE(silc_client_command_whois)
596 SilcClientCommandContext cmd = fsm_context;
597 SilcClientConnection conn = cmd->conn;
598 SilcClient client = conn->client;
599 SilcBuffer attrs = NULL;
600 unsigned char count[4], *tmp = NULL;
601 SilcBool details = FALSE, nick = FALSE;
602 unsigned char *pubkey = NULL;
605 /* Given without arguments fetches client's own information */
607 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
608 silc_buffer_data(conn->internal->local_idp),
609 silc_buffer_len(conn->internal->local_idp));
611 /* Notify application */
612 COMMAND(SILC_STATUS_OK);
614 /** Wait for command reply */
615 silc_fsm_next(fsm, silc_client_command_reply_wait);
616 return SILC_FSM_CONTINUE;
619 for (i = 1; i < cmd->argc; i++) {
620 if (!strcasecmp(cmd->argv[i], "-details")) {
622 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
623 pubkey = cmd->argv[i + 1];
626 /* We assume that the first parameter is the nickname, if it isn't
627 -details or -pubkey. The last parameter should always be the count */
630 } else if (i == cmd->argc - 1) {
631 int c = atoi(cmd->argv[i]);
632 SILC_PUT32_MSB(c, count);
639 /* If pubkey is set, add all attributes to the attrs buffer, except
642 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
643 SILC_ATTRIBUTE_SERVICE,
644 SILC_ATTRIBUTE_STATUS_MOOD,
645 SILC_ATTRIBUTE_STATUS_FREETEXT,
646 SILC_ATTRIBUTE_STATUS_MESSAGE,
647 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
648 SILC_ATTRIBUTE_PREFERRED_CONTACT,
649 SILC_ATTRIBUTE_TIMEZONE,
650 SILC_ATTRIBUTE_GEOLOCATION,
651 SILC_ATTRIBUTE_DEVICE_INFO,
652 SILC_ATTRIBUTE_USER_ICON, 0);
654 attrs = silc_client_attributes_request(0);
659 SilcAttributeObjPk obj;
662 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
663 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
664 "Could not load public key %s, check the filename",
666 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
670 switch (silc_pkcs_get_type(pk)) {
672 obj.type = "silc-rsa";
675 obj.type = "ssh-rsa";
677 case SILC_PKCS_X509V3:
678 obj.type = "x509v3-sign-rsa";
680 case SILC_PKCS_OPENPGP:
681 obj.type = "pgp-sign-rsa";
687 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
689 attrs = silc_attribute_payload_encode(attrs,
690 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
691 SILC_ATTRIBUTE_FLAG_VALID,
696 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
697 3, 1, nick ? cmd->argv[1] : NULL,
698 nick ? cmd->argv_lens[1] : 0,
699 2, tmp ? tmp : NULL, tmp ? 4 : 0,
700 3, silc_buffer_datalen(attrs));
702 /* Notify application */
703 COMMAND(SILC_STATUS_OK);
705 /** Wait for command reply */
706 silc_fsm_next(fsm, silc_client_command_reply_wait);
707 return SILC_FSM_CONTINUE;
710 return SILC_FSM_FINISH;
713 /******************************** WHOWAS ************************************/
715 /* Command WHOWAS. This command is used to query history information about
716 specific user that used to exist in the network. */
718 SILC_FSM_STATE(silc_client_command_whowas)
720 SilcClientCommandContext cmd = fsm_context;
721 SilcClientConnection conn = cmd->conn;
722 unsigned char count[4];
725 if (cmd->argc < 2 || cmd->argc > 3) {
726 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
727 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
728 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
729 SILC_STATUS_ERR_TOO_MANY_PARAMS));
730 return SILC_FSM_FINISH;
733 if (cmd->argc == 2) {
734 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
735 1, 1, cmd->argv[1], cmd->argv_lens[1]);
737 c = atoi(cmd->argv[2]);
738 SILC_PUT32_MSB(c, count);
739 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
740 2, 1, cmd->argv[1], cmd->argv_lens[1],
741 2, count, sizeof(count));
744 /* Notify application */
745 COMMAND(SILC_STATUS_OK);
747 /** Wait for command reply */
748 silc_fsm_next(fsm, silc_client_command_reply_wait);
749 return SILC_FSM_CONTINUE;
752 /******************************** IDENTIFY **********************************/
754 /* Command IDENTIFY. This command is used to query information about
755 specific user, especially ID's. */
757 SILC_FSM_STATE(silc_client_command_identify)
759 SilcClientCommandContext cmd = fsm_context;
760 SilcClientConnection conn = cmd->conn;
761 unsigned char count[4];
764 if (cmd->argc < 2 || cmd->argc > 3)
765 return SILC_FSM_FINISH;
767 if (cmd->argc == 2) {
768 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
769 1, 1, cmd->argv[1], cmd->argv_lens[1]);
771 c = atoi(cmd->argv[2]);
772 SILC_PUT32_MSB(c, count);
773 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
774 2, 1, cmd->argv[1], cmd->argv_lens[1],
775 4, count, sizeof(count));
778 /** Wait for command reply */
779 silc_fsm_next(fsm, silc_client_command_reply_wait);
780 return SILC_FSM_CONTINUE;
783 /********************************** NICK ************************************/
785 /* Command NICK. Shows current nickname/sets new nickname on current
788 SILC_FSM_STATE(silc_client_command_nick)
790 SilcClientCommandContext cmd = fsm_context;
791 SilcClientConnection conn = cmd->conn;
794 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
795 "Usage: /NICK <nickname>");
796 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
800 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
803 /* Show current nickname */
806 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
807 "Your nickname is %s on server %s",
808 conn->local_entry->nickname, conn->remote_host);
810 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
811 "Your nickname is %s", conn->local_entry->nickname);
814 COMMAND(SILC_STATUS_OK);
818 if (cmd->argv_lens[1] > 128)
819 cmd->argv_lens[1] = 128;
821 /* Send the NICK command */
822 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
823 1, 1, cmd->argv[1], cmd->argv_lens[1]);
825 /* Notify application */
826 COMMAND(SILC_STATUS_OK);
828 /** Wait for command reply */
829 silc_fsm_next(fsm, silc_client_command_reply_wait);
830 return SILC_FSM_CONTINUE;
833 return SILC_FSM_FINISH;
836 /********************************** LIST ************************************/
838 /* Command LIST. Lists channels on the current server. */
840 SILC_FSM_STATE(silc_client_command_list)
842 SilcClientCommandContext cmd = fsm_context;
843 SilcClientConnection conn = cmd->conn;
844 SilcChannelEntry channel;
845 SilcBuffer idp = NULL;
847 if (cmd->argc == 2) {
848 /* Get the Channel ID of the channel */
849 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
851 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
855 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
857 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
858 1, 1, silc_buffer_datalen(idp));
860 silc_buffer_free(idp);
862 /* Notify application */
863 COMMAND(SILC_STATUS_OK);
865 /** Wait for command reply */
866 silc_fsm_next(fsm, silc_client_command_reply_wait);
867 return SILC_FSM_CONTINUE;
870 /********************************** TOPIC ***********************************/
872 /* Command TOPIC. Sets/shows topic on a channel. */
874 SILC_FSM_STATE(silc_client_command_topic)
876 SilcClientCommandContext cmd = fsm_context;
877 SilcClientConnection conn = cmd->conn;
878 SilcChannelEntry channel;
882 if (cmd->argc < 2 || cmd->argc > 3) {
883 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
884 "Usage: /TOPIC <channel> [<topic>]");
885 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
886 SILC_STATUS_ERR_TOO_MANY_PARAMS));
890 if (cmd->argv[1][0] == '*') {
891 if (!conn->current_channel) {
892 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
895 name = conn->current_channel->channel_name;
900 if (!conn->current_channel) {
901 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
905 /* Get the Channel ID of the channel */
906 channel = silc_client_get_channel(conn->client, conn, name);
908 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
912 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
914 /* Send TOPIC command to the server */
916 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
917 1, silc_buffer_datalen(idp),
918 2, cmd->argv[2], strlen(cmd->argv[2]));
920 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
921 1, silc_buffer_datalen(idp));
923 silc_buffer_free(idp);
925 /* Notify application */
926 COMMAND(SILC_STATUS_OK);
928 /** Wait for command reply */
929 silc_fsm_next(fsm, silc_client_command_reply_wait);
930 return SILC_FSM_CONTINUE;
933 return SILC_FSM_FINISH;
936 /********************************* INVITE ***********************************/
938 /* Command INVITE. Invites specific client to join a channel. This is
939 also used to mange the invite list of the channel. */
941 SILC_FSM_STATE(silc_client_command_invite)
943 SilcClientCommandContext cmd = fsm_context;
944 SilcClientConnection conn = cmd->conn;
945 SilcClient client = conn->client;
946 SilcClientEntry client_entry = NULL;
947 SilcChannelEntry channel;
948 SilcBuffer clidp, chidp, args = NULL;
949 SilcPublicKey pubkey = NULL;
950 SilcDList clients = NULL;
951 char *nickname = NULL, *name;
953 unsigned char action[1];
956 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
957 "Usage: /INVITE <channel> [<nickname>[@server>]"
958 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
959 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
963 if (cmd->argv[1][0] == '*') {
964 if (!conn->current_channel) {
965 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
969 channel = conn->current_channel;
973 channel = silc_client_get_channel(conn->client, conn, name);
975 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
980 /* Parse the typed nickname. */
981 if (cmd->argc == 3) {
982 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
983 if (client->internal->params->nickname_parse)
984 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
986 nickname = strdup(cmd->argv[2]);
988 /* Find client entry */
989 clients = silc_client_get_clients_local(client, conn, nickname,
992 /* Resolve client information */
993 SILC_FSM_CALL(silc_client_get_clients(
994 client, conn, nickname,
996 silc_client_command_resolve_continue,
999 client_entry = silc_dlist_get(clients);
1001 if (cmd->argv[2][0] == '+')
1006 /* Check if it is public key file to be added to invite list */
1007 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1008 invite = cmd->argv[2];
1015 args = silc_buffer_alloc_size(2);
1016 silc_buffer_format(args,
1017 SILC_STR_UI_SHORT(1),
1020 chidp = silc_public_key_payload_encode(pubkey);
1021 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1022 silc_buffer_len(chidp), 2);
1023 silc_buffer_free(chidp);
1024 silc_pkcs_public_key_free(pubkey);
1026 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1030 /* Send the command */
1031 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1033 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1034 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1035 1, silc_buffer_datalen(chidp),
1036 2, silc_buffer_datalen(clidp),
1037 3, args ? action : NULL, args ? 1 : 0,
1038 4, silc_buffer_datalen(args));
1039 silc_buffer_free(clidp);
1041 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1042 1, silc_buffer_datalen(chidp),
1043 3, args ? action : NULL, args ? 1 : 0,
1044 4, silc_buffer_datalen(args));
1047 silc_buffer_free(chidp);
1048 silc_buffer_free(args);
1049 silc_free(nickname);
1050 silc_client_list_free(client, conn, clients);
1052 /* Notify application */
1053 COMMAND(SILC_STATUS_OK);
1055 /** Wait for command reply */
1056 silc_fsm_next(fsm, silc_client_command_reply_wait);
1057 return SILC_FSM_CONTINUE;
1060 silc_free(nickname);
1061 return SILC_FSM_FINISH;
1064 /********************************** QUIT ************************************/
1066 /* Close the connection */
1068 SILC_FSM_STATE(silc_client_command_quit_final)
1070 SilcClientCommandContext cmd = fsm_context;
1071 SilcClientConnection conn = cmd->conn;
1072 SilcClient client = conn->client;
1074 /* Notify application */
1075 COMMAND(SILC_STATUS_OK);
1077 /* Call connection callback */
1078 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
1079 0, NULL, conn->callback_context);
1081 /* Signal to close connection */
1082 if (!conn->internal->disconnected) {
1083 conn->internal->disconnected = TRUE;
1084 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
1087 return SILC_FSM_FINISH;
1090 /* Command QUIT. Closes connection with current server. */
1092 SILC_FSM_STATE(silc_client_command_quit)
1094 SilcClientCommandContext cmd = fsm_context;
1095 SilcClientConnection conn = cmd->conn;
1098 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1099 1, cmd->argv[1], cmd->argv_lens[1]);
1101 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1103 /* Sleep for a while */
1106 /* We close the connection with a little timeout */
1107 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1108 return SILC_FSM_WAIT;
1111 /********************************** KILL ************************************/
1114 /* Command KILL. Router operator can use this command to remove an client
1115 fromthe SILC Network. */
1117 SILC_FSM_STATE(silc_client_command_kill)
1119 SilcClientCommandContext cmd = fsm_context;
1120 SilcClientConnection conn = cmd->conn;
1121 SilcClient client = conn->client;
1122 SilcBuffer idp, auth = NULL;
1123 SilcClientEntry target;
1125 char *nickname = NULL, *comment = NULL;
1127 if (cmd->argc < 2) {
1128 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1129 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1130 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1131 return SILC_FSM_FINISH;
1134 /* Parse the typed nickname. */
1135 if (client->internal->params->nickname_parse)
1136 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
1138 nickname = strdup(cmd->argv[1]);
1140 return SILC_FSM_FINISH;
1142 /* Get the target client */
1143 clients = silc_client_get_clients_local(client, conn, nickname,
1146 /* Resolve client information */
1147 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
1149 silc_client_command_resolve_continue,
1152 target = silc_dlist_get(clients);
1154 if (cmd->argc >= 3) {
1155 if (strcasecmp(cmd->argv[2], "-pubkey"))
1156 comment = cmd->argv[2];
1158 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1159 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1160 /* Encode the public key authentication payload */
1161 auth = silc_auth_public_key_auth_generate(conn->public_key,
1164 conn->internal->sha1hash,
1165 &target->id, SILC_ID_CLIENT);
1169 /* Send the KILL command to the server */
1170 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1171 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1172 1, silc_buffer_datalen(idp),
1173 2, comment, comment ? strlen(comment) : 0,
1174 3, silc_buffer_datalen(auth));
1175 silc_buffer_free(idp);
1176 silc_buffer_free(auth);
1177 silc_free(nickname);
1178 silc_client_list_free(client, conn, clients);
1180 /* Notify application */
1181 COMMAND(SILC_STATUS_OK);
1183 /** Wait for command reply */
1184 silc_fsm_next(fsm, silc_client_command_reply_wait);
1185 return SILC_FSM_CONTINUE;
1188 /********************************** INFO ************************************/
1190 /* Command INFO. Request information about specific server. If specific
1191 server is not provided the current server is used. */
1193 SILC_FSM_STATE(silc_client_command_info)
1195 SilcClientCommandContext cmd = fsm_context;
1196 SilcClientConnection conn = cmd->conn;
1198 /* Send the command */
1200 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1201 1, cmd->argv[1], cmd->argv_lens[1]);
1203 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1205 /* Notify application */
1206 COMMAND(SILC_STATUS_OK);
1208 /** Wait for command reply */
1209 silc_fsm_next(fsm, silc_client_command_reply_wait);
1210 return SILC_FSM_CONTINUE;
1213 /********************************** STATS ***********************************/
1215 /* Command STATS. Shows server and network statistics. */
1217 SILC_FSM_STATE(silc_client_command_stats)
1219 SilcClientCommandContext cmd = fsm_context;
1220 SilcClientConnection conn = cmd->conn;
1222 /* Send the command */
1223 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1224 1, silc_buffer_datalen(conn->internal->
1227 /* Notify application */
1228 COMMAND(SILC_STATUS_OK);
1230 /** Wait for command reply */
1231 silc_fsm_next(fsm, silc_client_command_reply_wait);
1232 return SILC_FSM_CONTINUE;
1235 /********************************** PING ************************************/
1237 /* Command PING. Sends ping to server. */
1239 SILC_FSM_STATE(silc_client_command_ping)
1241 SilcClientCommandContext cmd = fsm_context;
1242 SilcClientConnection conn = cmd->conn;
1244 if (cmd->argc < 2) {
1245 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1246 return SILC_FSM_FINISH;
1249 /* Send the command */
1250 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1251 1, silc_buffer_datalen(conn->internal->
1254 /* Save ping time */
1255 cmd->context = SILC_64_TO_PTR(silc_time());
1257 /* Notify application */
1258 COMMAND(SILC_STATUS_OK);
1260 /** Wait for command reply */
1261 silc_fsm_next(fsm, silc_client_command_reply_wait);
1262 return SILC_FSM_CONTINUE;
1265 /********************************** JOIN ************************************/
1267 /* Command JOIN. Joins to a channel. */
1269 SILC_FSM_STATE(silc_client_command_join)
1271 SilcClientCommandContext cmd = fsm_context;
1272 SilcClientConnection conn = cmd->conn;
1273 SilcChannelEntry channel;
1274 SilcBuffer auth = NULL, cauth = NULL;
1275 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1276 int i, passphrase_len = 0;
1278 if (cmd->argc < 2) {
1279 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1283 /* See if we have joined to the requested channel already */
1284 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1285 if (channel && silc_client_on_channel(channel, conn->local_entry))
1288 if (cmd->argv_lens[1] > 256)
1289 cmd->argv_lens[1] = 256;
1291 name = cmd->argv[1];
1293 for (i = 2; i < cmd->argc; i++) {
1294 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1295 cipher = cmd->argv[++i];
1296 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1297 hmac = cmd->argv[++i];
1298 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1299 auth = silc_auth_public_key_auth_generate(conn->public_key,
1302 conn->internal->sha1hash,
1305 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1306 SilcPublicKey pubkey = conn->public_key;
1307 SilcPrivateKey privkey = conn->private_key;
1308 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1311 if (cmd->argc >= i + 3) {
1313 if (cmd->argc >= i + 4) {
1314 pass = cmd->argv[i + 3];
1317 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1318 &pubkey, &privkey)) {
1319 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1320 "Could not load key pair, check your arguments");
1321 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1327 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1328 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1330 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1331 memcpy(pubdata, pkhash, 20);
1332 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1334 conn->internal->sha1hash,
1337 memset(pubdata, 0, 128);
1340 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1341 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1342 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1343 cmd->argv_lens[i], 0);
1344 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1345 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1346 0, pu8, passphrase_len);
1349 passphrase = strdup(cmd->argv[i]);
1350 passphrase_len = cmd->argv_lens[i];
1355 /* Send JOIN command to the server */
1356 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1357 1, name, strlen(name),
1358 2, silc_buffer_datalen(conn->internal->
1360 3, passphrase, passphrase_len,
1361 4, cipher, cipher ? strlen(cipher) : 0,
1362 5, hmac, hmac ? strlen(hmac) : 0,
1363 6, silc_buffer_datalen(auth),
1364 7, silc_buffer_datalen(cauth));
1366 silc_buffer_free(auth);
1367 silc_buffer_free(cauth);
1369 memset(passphrase, 0, strlen(passphrase));
1370 silc_free(passphrase);
1372 /* Notify application */
1373 COMMAND(SILC_STATUS_OK);
1375 /** Wait for command reply */
1376 silc_fsm_next(fsm, silc_client_command_reply_wait);
1377 return SILC_FSM_CONTINUE;
1380 return SILC_FSM_FINISH;
1383 /********************************** MOTD ************************************/
1385 /* MOTD command. Requests motd from server. */
1387 SILC_FSM_STATE(silc_client_command_motd)
1389 SilcClientCommandContext cmd = fsm_context;
1390 SilcClientConnection conn = cmd->conn;
1392 if (cmd->argc < 1 || cmd->argc > 2) {
1393 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1394 "Usage: /MOTD [<server>]");
1395 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1396 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1397 return SILC_FSM_FINISH;
1400 /* Send the command */
1402 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1403 1, conn->remote_host,
1404 strlen(conn->remote_host));
1406 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1407 1, cmd->argv[1], cmd->argv_lens[1]);
1409 /* Notify application */
1410 COMMAND(SILC_STATUS_OK);
1412 /** Wait for command reply */
1413 silc_fsm_next(fsm, silc_client_command_reply_wait);
1414 return SILC_FSM_CONTINUE;
1417 /********************************** UMODE ***********************************/
1419 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1420 modes as client cannot set itself server/router operator privileges. */
1422 SILC_FSM_STATE(silc_client_command_umode)
1424 SilcClientCommandContext cmd = fsm_context;
1425 SilcClientConnection conn = cmd->conn;
1426 unsigned char *cp, modebuf[4];
1427 SilcUInt32 mode, add, len;
1430 if (cmd->argc < 2) {
1431 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1432 "Usage: /UMODE +|-<modes>");
1433 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1434 return SILC_FSM_FINISH;
1437 mode = conn->local_entry->mode;
1439 /* Are we adding or removing mode */
1440 if (cmd->argv[1][0] == '-')
1446 cp = cmd->argv[1] + 1;
1448 for (i = 0; i < len; i++) {
1453 mode |= SILC_UMODE_SERVER_OPERATOR;
1454 mode |= SILC_UMODE_ROUTER_OPERATOR;
1455 mode |= SILC_UMODE_GONE;
1456 mode |= SILC_UMODE_INDISPOSED;
1457 mode |= SILC_UMODE_BUSY;
1458 mode |= SILC_UMODE_PAGE;
1459 mode |= SILC_UMODE_HYPER;
1460 mode |= SILC_UMODE_ROBOT;
1461 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1462 mode |= SILC_UMODE_REJECT_WATCHING;
1464 mode = SILC_UMODE_NONE;
1469 mode |= SILC_UMODE_SERVER_OPERATOR;
1471 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1475 mode |= SILC_UMODE_ROUTER_OPERATOR;
1477 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1481 mode |= SILC_UMODE_GONE;
1483 mode &= ~SILC_UMODE_GONE;
1487 mode |= SILC_UMODE_INDISPOSED;
1489 mode &= ~SILC_UMODE_INDISPOSED;
1493 mode |= SILC_UMODE_BUSY;
1495 mode &= ~SILC_UMODE_BUSY;
1499 mode |= SILC_UMODE_PAGE;
1501 mode &= ~SILC_UMODE_PAGE;
1505 mode |= SILC_UMODE_HYPER;
1507 mode &= ~SILC_UMODE_HYPER;
1511 mode |= SILC_UMODE_ROBOT;
1513 mode &= ~SILC_UMODE_ROBOT;
1517 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1519 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1523 mode |= SILC_UMODE_REJECT_WATCHING;
1525 mode &= ~SILC_UMODE_REJECT_WATCHING;
1529 mode |= SILC_UMODE_BLOCK_INVITE;
1531 mode &= ~SILC_UMODE_BLOCK_INVITE;
1534 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1535 return SILC_FSM_FINISH;
1540 SILC_PUT32_MSB(mode, modebuf);
1542 /* Send the command */
1543 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1544 1, silc_buffer_datalen(conn->internal->
1546 2, modebuf, sizeof(modebuf));
1548 /* Notify application */
1549 COMMAND(SILC_STATUS_OK);
1551 /** Wait for command reply */
1552 silc_fsm_next(fsm, silc_client_command_reply_wait);
1553 return SILC_FSM_CONTINUE;
1556 /********************************** CMODE ***********************************/
1558 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1559 can be set several at once. Those modes that require argument must be set
1560 separately (unless set with modes that does not require arguments). */
1562 SILC_FSM_STATE(silc_client_command_cmode)
1564 SilcClientCommandContext cmd = fsm_context;
1565 SilcClientConnection conn = cmd->conn;
1566 SilcClient client = conn->client;
1567 SilcChannelEntry channel;
1568 SilcBuffer chidp, auth = NULL, pk = NULL;
1569 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1570 SilcUInt32 mode, add, type, len, arg_len = 0;
1573 if (cmd->argc < 3) {
1574 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1575 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1576 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1580 if (cmd->argv[1][0] == '*') {
1581 if (!conn->current_channel) {
1582 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1586 channel = conn->current_channel;
1588 name = cmd->argv[1];
1590 channel = silc_client_get_channel(conn->client, conn, name);
1592 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1597 mode = channel->mode;
1599 /* Are we adding or removing mode */
1600 if (cmd->argv[2][0] == '-')
1605 /* Argument type to be sent to server */
1609 cp = cmd->argv[2] + 1;
1611 for (i = 0; i < len; i++) {
1615 mode |= SILC_CHANNEL_MODE_PRIVATE;
1617 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1621 mode |= SILC_CHANNEL_MODE_SECRET;
1623 mode &= ~SILC_CHANNEL_MODE_SECRET;
1627 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1629 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1633 mode |= SILC_CHANNEL_MODE_INVITE;
1635 mode &= ~SILC_CHANNEL_MODE_INVITE;
1639 mode |= SILC_CHANNEL_MODE_TOPIC;
1641 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1645 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1647 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1651 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1653 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1658 mode |= SILC_CHANNEL_MODE_ULIMIT;
1660 if (cmd->argc < 4) {
1661 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1662 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1663 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1666 ll = atoi(cmd->argv[3]);
1667 SILC_PUT32_MSB(ll, tmp);
1671 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1676 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1678 if (cmd->argc < 4) {
1679 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1680 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1681 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1685 arg_len = cmd->argv_lens[3];
1687 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1692 mode |= SILC_CHANNEL_MODE_CIPHER;
1694 if (cmd->argc < 4) {
1695 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1696 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1697 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1701 arg_len = cmd->argv_lens[3];
1703 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1708 mode |= SILC_CHANNEL_MODE_HMAC;
1710 if (cmd->argc < 4) {
1711 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1712 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1713 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1717 arg_len = cmd->argv_lens[3];
1719 mode &= ~SILC_CHANNEL_MODE_HMAC;
1724 SilcPublicKey pubkey = conn->public_key;
1725 SilcPrivateKey privkey = conn->private_key;
1727 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1730 if (cmd->argc >= 5) {
1733 pass = cmd->argv[5];
1734 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1735 &pubkey, &privkey)) {
1736 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1737 "Could not load key pair, check your arguments");
1738 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1743 pk = silc_public_key_payload_encode(pubkey);
1744 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1746 conn->internal->sha1hash,
1749 arg = silc_buffer_data(auth);
1750 arg_len = silc_buffer_len(auth);
1752 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1758 SilcBool chadd = FALSE;
1759 SilcPublicKey chpk = NULL;
1761 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1764 if (cmd->argc == 3) {
1765 /* Send empty command to receive the public key list. */
1766 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1767 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1769 1, silc_buffer_datalen(chidp));
1770 silc_buffer_free(chidp);
1772 /* Notify application */
1773 COMMAND(SILC_STATUS_OK);
1777 if (cmd->argc >= 4) {
1778 auth = silc_buffer_alloc_size(2);
1779 silc_buffer_format(auth,
1780 SILC_STR_UI_SHORT(cmd->argc - 3),
1784 for (k = 3; k < cmd->argc; k++) {
1785 if (cmd->argv[k][0] == '+')
1787 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1788 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1789 "Could not load public key %s, check the filename",
1791 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1792 silc_buffer_free(auth);
1797 pk = silc_public_key_payload_encode(chpk);
1798 auth = silc_argument_payload_encode_one(auth,
1799 silc_buffer_datalen(pk),
1800 chadd ? 0x00 : 0x01);
1801 silc_pkcs_public_key_free(chpk);
1802 silc_buffer_free(pk);
1807 arg = silc_buffer_data(auth);
1808 arg_len = silc_buffer_len(auth);
1810 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1814 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1820 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1821 SILC_PUT32_MSB(mode, modebuf);
1823 /* Send the command. We support sending only one mode at once that
1824 requires an argument. */
1826 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1827 1, silc_buffer_datalen(chidp),
1828 2, modebuf, sizeof(modebuf),
1830 8, silc_buffer_datalen(pk));
1832 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1833 1, silc_buffer_datalen(chidp),
1834 2, modebuf, sizeof(modebuf));
1837 silc_buffer_free(chidp);
1838 silc_buffer_free(auth);
1839 silc_buffer_free(pk);
1841 /* Notify application */
1842 COMMAND(SILC_STATUS_OK);
1844 /** Wait for command reply */
1845 silc_fsm_next(fsm, silc_client_command_reply_wait);
1846 return SILC_FSM_CONTINUE;
1849 return SILC_FSM_FINISH;
1852 /********************************* CUMODE ***********************************/
1854 /* CUMODE command. Changes client's mode on a channel. */
1856 SILC_FSM_STATE(silc_client_command_cumode)
1858 SilcClientCommandContext cmd = fsm_context;
1859 SilcClientConnection conn = cmd->conn;
1860 SilcClient client = conn->client;
1861 SilcChannelEntry channel;
1862 SilcChannelUser chu;
1863 SilcClientEntry client_entry;
1864 SilcBuffer clidp, chidp, auth = NULL;
1865 SilcDList clients = NULL;
1866 unsigned char *name, *cp, modebuf[4];
1867 SilcUInt32 mode = 0, add, len;
1868 char *nickname = NULL;
1871 if (cmd->argc < 4) {
1872 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1873 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1874 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1878 if (cmd->argv[1][0] == '*') {
1879 if (!conn->current_channel) {
1880 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1884 channel = conn->current_channel;
1886 name = cmd->argv[1];
1888 channel = silc_client_get_channel(conn->client, conn, name);
1890 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1895 /* Parse the typed nickname. */
1896 if (client->internal->params->nickname_parse)
1897 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1899 nickname = strdup(cmd->argv[3]);
1901 /* Find client entry */
1902 clients = silc_client_get_clients_local(client, conn, nickname,
1905 /* Resolve client information */
1906 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1907 silc_client_command_resolve_continue,
1910 client_entry = silc_dlist_get(clients);
1912 /* Get the current mode */
1913 chu = silc_client_on_channel(channel, client_entry);
1917 /* Are we adding or removing mode */
1918 if (cmd->argv[2][0] == '-')
1924 cp = cmd->argv[2] + 1;
1926 for (i = 0; i < len; i++) {
1930 mode |= SILC_CHANNEL_UMODE_CHANFO;
1931 mode |= SILC_CHANNEL_UMODE_CHANOP;
1932 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1933 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1934 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1936 mode = SILC_CHANNEL_UMODE_NONE;
1941 SilcPublicKey pubkey = conn->public_key;
1942 SilcPrivateKey privkey = conn->private_key;
1944 if (cmd->argc >= 6) {
1947 pass = cmd->argv[6];
1948 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1949 &pubkey, &privkey)) {
1950 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1951 "Could not load key pair, check your arguments");
1952 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1957 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1959 conn->internal->sha1hash,
1962 mode |= SILC_CHANNEL_UMODE_CHANFO;
1964 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1969 mode |= SILC_CHANNEL_UMODE_CHANOP;
1971 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1975 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1977 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1981 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1983 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1987 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1989 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1993 mode |= SILC_CHANNEL_UMODE_QUIET;
1995 mode &= ~SILC_CHANNEL_UMODE_QUIET;
1998 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2004 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2005 SILC_PUT32_MSB(mode, modebuf);
2006 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2008 /* Send the command packet. We support sending only one mode at once
2009 that requires an argument. */
2010 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2011 1, silc_buffer_datalen(chidp),
2013 3, silc_buffer_datalen(clidp),
2014 4, silc_buffer_datalen(auth));
2016 silc_buffer_free(chidp);
2017 silc_buffer_free(clidp);
2019 silc_buffer_free(auth);
2020 silc_free(nickname);
2021 silc_client_list_free(client, conn, clients);
2023 /* Notify application */
2024 COMMAND(SILC_STATUS_OK);
2026 /** Wait for command reply */
2027 silc_fsm_next(fsm, silc_client_command_reply_wait);
2028 return SILC_FSM_CONTINUE;
2031 silc_client_list_free(client, conn, clients);
2032 silc_free(nickname);
2033 return SILC_FSM_FINISH;
2036 /********************************** KICK ************************************/
2038 /* KICK command. Kicks a client out of channel. */
2040 SILC_FSM_STATE(silc_client_command_kick)
2042 SilcClientCommandContext cmd = fsm_context;
2043 SilcClientConnection conn = cmd->conn;
2044 SilcClient client = conn->client;
2045 SilcChannelEntry channel;
2046 SilcBuffer idp, idp2;
2047 SilcClientEntry target;
2048 SilcDList clients = NULL;
2050 char *nickname = NULL;
2052 if (cmd->argc < 3) {
2053 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2054 "Usage: /KICK <channel> <nickname> [<comment>]");
2055 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2059 if (cmd->argv[1][0] == '*') {
2060 if (!conn->current_channel) {
2061 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2064 name = conn->current_channel->channel_name;
2066 name = cmd->argv[1];
2069 if (!conn->current_channel) {
2070 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2074 /* Get the Channel ID of the channel */
2075 channel = silc_client_get_channel(conn->client, conn, name);
2077 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2081 /* Parse the typed nickname. */
2082 if (client->internal->params->nickname_parse)
2083 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
2085 nickname = strdup(cmd->argv[2]);
2087 /* Get the target client */
2088 clients = silc_client_get_clients_local(client, conn, nickname,
2091 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2092 "No such client: %s", cmd->argv[2]);
2093 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2096 target = silc_dlist_get(clients);
2098 /* Send KICK command to the server */
2099 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2100 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2102 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2103 1, silc_buffer_datalen(idp),
2104 2, silc_buffer_datalen(idp2));
2106 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2107 1, silc_buffer_datalen(idp),
2108 2, silc_buffer_datalen(idp2),
2109 3, cmd->argv[3], strlen(cmd->argv[3]));
2111 silc_buffer_free(idp);
2112 silc_buffer_free(idp2);
2113 silc_free(nickname);
2114 silc_client_list_free(client, conn, clients);
2116 /* Notify application */
2117 COMMAND(SILC_STATUS_OK);
2119 /** Wait for command reply */
2120 silc_fsm_next(fsm, silc_client_command_reply_wait);
2121 return SILC_FSM_CONTINUE;
2124 silc_free(nickname);
2125 return SILC_FSM_FINISH;
2128 /***************************** OPER & SILCOPER ******************************/
2131 unsigned char *passphrase;
2132 SilcUInt32 passphrase_len;
2133 } *SilcClientCommandOper;
2135 /* Ask passphrase callback */
2137 static void silc_client_command_oper_cb(unsigned char *data,
2138 SilcUInt32 data_len, void *context)
2140 SilcClientCommandContext cmd = context;
2141 SilcClientCommandOper oper = cmd->context;
2143 if (data && data_len)
2144 oper->passphrase = silc_memdup(data, data_len);
2145 oper->passphrase_len = data_len;
2148 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2151 /* Send OPER/SILCOPER command */
2153 SILC_FSM_STATE(silc_client_command_oper_send)
2155 SilcClientCommandContext cmd = fsm_context;
2156 SilcClientConnection conn = cmd->conn;
2157 SilcClientCommandOper oper = cmd->context;
2160 if (!oper || !oper->passphrase) {
2161 /* Encode the public key authentication payload */
2162 auth = silc_auth_public_key_auth_generate(conn->public_key,
2165 conn->internal->hash,
2169 /* Encode the password authentication payload */
2170 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2171 oper->passphrase, oper->passphrase_len);
2174 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2175 1, cmd->argv[1], strlen(cmd->argv[1]),
2176 2, silc_buffer_datalen(auth));
2178 silc_buffer_clear(auth);
2179 silc_buffer_free(auth);
2181 silc_free(oper->passphrase);
2185 /* Notify application */
2186 COMMAND(SILC_STATUS_OK);
2188 /** Wait for command reply */
2189 silc_fsm_next(fsm, silc_client_command_reply_wait);
2190 return SILC_FSM_CONTINUE;
2193 /* OPER command. Used to obtain server operator privileges. */
2195 SILC_FSM_STATE(silc_client_command_oper)
2197 SilcClientCommandContext cmd = fsm_context;
2198 SilcClientConnection conn = cmd->conn;
2199 SilcClientCommandOper oper;
2201 if (cmd->argc < 2) {
2202 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2203 "Usage: /OPER <username> [-pubkey]");
2204 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2205 return SILC_FSM_FINISH;
2208 /* Get passphrase */
2209 if (cmd->argc < 3) {
2210 oper = silc_calloc(1, sizeof(*oper));
2212 return SILC_FSM_FINISH;
2213 cmd->context = oper;
2214 SILC_FSM_CALL(conn->client->internal->
2215 ops->ask_passphrase(conn->client, conn,
2216 silc_client_command_oper_cb, cmd));
2219 silc_fsm_next(fsm, silc_client_command_oper_send);
2220 return SILC_FSM_CONTINUE;
2223 /* SILCOPER command. Used to obtain router operator privileges. */
2225 SILC_FSM_STATE(silc_client_command_silcoper)
2227 SilcClientCommandContext cmd = fsm_context;
2228 SilcClientConnection conn = cmd->conn;
2229 SilcClientCommandOper oper;
2231 if (cmd->argc < 2) {
2232 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2233 "Usage: /SILCOPER <username> [-pubkey]");
2234 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2235 return SILC_FSM_FINISH;
2238 /* Get passphrase */
2239 if (cmd->argc < 3) {
2240 oper = silc_calloc(1, sizeof(*oper));
2242 return SILC_FSM_FINISH;
2243 cmd->context = oper;
2244 SILC_FSM_CALL(conn->client->internal->
2245 ops->ask_passphrase(conn->client, conn,
2246 silc_client_command_oper_cb, cmd));
2249 silc_fsm_next(fsm, silc_client_command_oper_send);
2250 return SILC_FSM_CONTINUE;
2253 /*********************************** BAN ************************************/
2255 /* Command BAN. This is used to manage the ban list of the channel. */
2257 SILC_FSM_STATE(silc_client_command_ban)
2259 SilcClientCommandContext cmd = fsm_context;
2260 SilcClientConnection conn = cmd->conn;
2261 SilcChannelEntry channel;
2262 SilcBuffer chidp, args = NULL;
2263 char *name, *ban = NULL;
2264 unsigned char action[1];
2265 SilcPublicKey pubkey = NULL;
2267 if (cmd->argc < 2) {
2268 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2269 "Usage: /BAN <channel> "
2270 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2271 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2275 if (cmd->argv[1][0] == '*') {
2276 if (!conn->current_channel) {
2277 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2281 channel = conn->current_channel;
2283 name = cmd->argv[1];
2285 channel = silc_client_get_channel(conn->client, conn, name);
2287 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2292 if (cmd->argc == 3) {
2293 if (cmd->argv[2][0] == '+')
2298 /* Check if it is public key file to be added to invite list */
2299 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2306 args = silc_buffer_alloc_size(2);
2307 silc_buffer_format(args,
2308 SILC_STR_UI_SHORT(1),
2311 chidp = silc_public_key_payload_encode(pubkey);
2312 args = silc_argument_payload_encode_one(args,
2313 silc_buffer_datalen(chidp), 2);
2314 silc_buffer_free(chidp);
2315 silc_pkcs_public_key_free(pubkey);
2317 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2321 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2323 /* Send the command */
2324 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2325 1, silc_buffer_datalen(chidp),
2326 2, args ? action : NULL, args ? 1 : 0,
2327 3, silc_buffer_datalen(args));
2329 silc_buffer_free(chidp);
2330 silc_buffer_free(args);
2332 /* Notify application */
2333 COMMAND(SILC_STATUS_OK);
2335 /** Wait for command reply */
2336 silc_fsm_next(fsm, silc_client_command_reply_wait);
2337 return SILC_FSM_CONTINUE;
2340 return SILC_FSM_FINISH;
2343 /********************************* DETACH ***********************************/
2345 /* Command DETACH. This is used to detach from the server */
2347 SILC_FSM_STATE(silc_client_command_detach)
2349 SilcClientCommandContext cmd = fsm_context;
2350 SilcClientConnection conn = cmd->conn;
2352 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2354 /* Notify application */
2355 COMMAND(SILC_STATUS_OK);
2357 /** Wait for command reply */
2358 silc_fsm_next(fsm, silc_client_command_reply_wait);
2359 return SILC_FSM_CONTINUE;
2362 /********************************** WATCH ***********************************/
2364 /* Command WATCH. */
2366 SILC_FSM_STATE(silc_client_command_watch)
2368 SilcClientCommandContext cmd = fsm_context;
2369 SilcClientConnection conn = cmd->conn;
2370 SilcBuffer args = NULL;
2372 const char *pubkey = NULL;
2373 SilcBool pubkey_add = TRUE;
2375 if (cmd->argc < 3) {
2376 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2380 if (!strcasecmp(cmd->argv[1], "-add")) {
2382 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2384 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2386 pubkey = cmd->argv[2] + 1;
2387 if (cmd->argv[2][0] == '-')
2390 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2398 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2399 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2400 "Could not load public key %s, check the filename", pubkey);
2401 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2405 args = silc_buffer_alloc_size(2);
2406 silc_buffer_format(args,
2407 SILC_STR_UI_SHORT(1),
2409 buffer = silc_public_key_payload_encode(pk);
2410 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2411 pubkey_add ? 0x00 : 0x01);
2412 silc_buffer_free(buffer);
2413 silc_pkcs_public_key_free(pk);
2416 /* Send the commmand */
2417 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2418 1, silc_buffer_datalen(conn->internal->
2420 type, pubkey ? args->data : cmd->argv[2],
2421 pubkey ? silc_buffer_len(args) :
2424 silc_buffer_free(args);
2426 /* Notify application */
2427 COMMAND(SILC_STATUS_OK);
2429 /** Wait for command reply */
2430 silc_fsm_next(fsm, silc_client_command_reply_wait);
2431 return SILC_FSM_CONTINUE;
2434 return SILC_FSM_FINISH;
2437 /********************************** LEAVE ***********************************/
2439 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2441 SILC_FSM_STATE(silc_client_command_leave)
2443 SilcClientCommandContext cmd = fsm_context;
2444 SilcClientConnection conn = cmd->conn;
2445 SilcChannelEntry channel;
2449 if (cmd->argc != 2) {
2450 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2451 "Usage: /LEAVE <channel>");
2452 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2456 if (cmd->argv[1][0] == '*') {
2457 if (!conn->current_channel) {
2458 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2461 name = conn->current_channel->channel_name;
2463 name = cmd->argv[1];
2466 /* Get the channel entry */
2467 channel = silc_client_get_channel(conn->client, conn, name);
2469 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2473 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2475 /* Send LEAVE command to the server */
2476 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2477 1, silc_buffer_datalen(idp));
2479 silc_buffer_free(idp);
2481 /* Notify application */
2482 COMMAND(SILC_STATUS_OK);
2484 if (conn->current_channel == channel)
2485 conn->current_channel = NULL;
2487 /** Wait for command reply */
2488 silc_fsm_next(fsm, silc_client_command_reply_wait);
2489 return SILC_FSM_CONTINUE;
2492 return SILC_FSM_FINISH;
2495 /********************************** USERS ***********************************/
2497 /* Command USERS. Requests the USERS of the clients joined on requested
2500 SILC_FSM_STATE(silc_client_command_users)
2502 SilcClientCommandContext cmd = fsm_context;
2503 SilcClientConnection conn = cmd->conn;
2506 if (cmd->argc != 2) {
2507 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2508 "Usage: /USERS <channel>");
2509 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2513 if (cmd->argv[1][0] == '*') {
2514 if (!conn->current_channel) {
2515 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2518 name = conn->current_channel->channel_name;
2520 name = cmd->argv[1];
2523 /* Send USERS command to the server */
2524 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2525 2, name, strlen(name));
2527 /* Notify application */
2528 COMMAND(SILC_STATUS_OK);
2530 /** Wait for command reply */
2531 silc_fsm_next(fsm, silc_client_command_reply_wait);
2532 return SILC_FSM_CONTINUE;
2535 return SILC_FSM_FINISH;
2538 /********************************* GETKEY ***********************************/
2540 /* Command GETKEY. Used to fetch remote client's public key. */
2542 SILC_FSM_STATE(silc_client_command_getkey)
2544 SilcClientCommandContext cmd = fsm_context;
2545 SilcClientConnection conn = cmd->conn;
2546 SilcClient client = conn->client;
2547 SilcClientEntry client_entry;
2548 SilcServerEntry server_entry;
2550 char *nickname = NULL;
2553 if (cmd->argc < 2) {
2554 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2555 "Usage: /GETKEY <nickname or server name>");
2556 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2557 return SILC_FSM_FINISH;
2560 /* Parse the typed nickname. */
2561 if (client->internal->params->nickname_parse)
2562 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2564 nickname = strdup(cmd->argv[1]);
2566 COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2567 return SILC_FSM_FINISH;
2570 /* Find client entry */
2571 clients = silc_client_get_clients_local(client, conn, nickname,
2574 /* Check whether user requested server */
2575 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2576 if (!server_entry) {
2577 if (cmd->resolved) {
2578 /* Resolving didn't find anything. We should never get here as
2579 errors are handled in the resolving callback. */
2580 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2581 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2582 return SILC_FSM_FINISH;
2585 /* No client or server exist with this name, query for both. */
2586 cmd->resolved = TRUE;
2587 SILC_FSM_CALL(silc_client_command_send(client, conn,
2588 SILC_COMMAND_IDENTIFY,
2589 silc_client_command_continue,
2592 strlen(cmd->argv[1]),
2594 strlen(cmd->argv[1])));
2597 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2598 silc_client_unref_server(client, conn, server_entry);
2600 client_entry = silc_dlist_get(clients);
2601 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2602 silc_client_list_free(client, conn, clients);
2605 /* Send the commmand */
2606 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2607 1, silc_buffer_datalen(idp));
2609 silc_buffer_free(idp);
2610 silc_free(nickname);
2612 /* Notify application */
2613 COMMAND(SILC_STATUS_OK);
2615 /** Wait for command reply */
2616 silc_fsm_next(fsm, silc_client_command_reply_wait);
2617 return SILC_FSM_CONTINUE;
2620 /********************************* SERVICE **********************************/
2622 /* Command SERVICE. Negotiates service agreement with server. */
2623 /* XXX incomplete */
2625 SILC_FSM_STATE(silc_client_command_service)
2627 SilcClientCommandContext cmd = fsm_context;
2629 SilcClientConnection conn = cmd->conn;
2633 if (cmd->argc < 2) {
2634 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2635 "Usage: /SERVICE [<service name>] [-pubkey]");
2636 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2637 return SILC_FSM_FINISH;
2640 name = cmd->argv[1];
2642 /* Send SERVICE command to the server */
2643 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2644 ++conn->cmd_ident, 1,
2645 1, name, strlen(name));
2646 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2647 NULL, 0, NULL, NULL, buffer->data,
2649 silc_buffer_free(buffer);
2652 /* Notify application */
2653 COMMAND(SILC_STATUS_OK);
2655 /** Wait for command reply */
2656 silc_fsm_next(fsm, silc_client_command_reply_wait);
2657 return SILC_FSM_CONTINUE;
2660 /* Register all default commands provided by the client library for the
2663 void silc_client_commands_register(SilcClient client)
2665 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2668 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2669 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2670 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2671 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2672 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2673 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2674 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2675 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2676 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2677 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2678 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2679 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2680 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2681 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2682 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2683 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2684 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2685 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2686 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2687 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2688 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2689 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2690 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2691 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2692 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2693 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2694 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2697 /* Unregister all commands. */
2699 void silc_client_commands_unregister(SilcClient client)
2701 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2702 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2703 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2704 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2705 SILC_CLIENT_CMDU(list, LIST, "LIST");
2706 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2707 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2708 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2709 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2710 SILC_CLIENT_CMDU(info, INFO, "INFO");
2711 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2712 SILC_CLIENT_CMDU(ping, PING, "PING");
2713 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2714 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2715 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2716 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2717 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2718 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2719 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2720 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2721 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2722 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2723 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2724 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2725 SILC_CLIENT_CMDU(users, USERS, "USERS");
2726 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2727 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2730 /****************** Client Side Incoming Command Handling *******************/
2732 /* Reply to WHOIS command from server */
2734 static void silc_client_command_process_whois(SilcClient client,
2735 SilcClientConnection conn,
2736 SilcCommandPayload payload,
2737 SilcArgumentPayload args)
2743 SilcBuffer buffer, packet;
2745 SILC_LOG_DEBUG(("Received WHOIS command"));
2747 /* Try to take the Requested Attributes */
2748 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2752 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2756 /* Process requested attributes */
2757 buffer = silc_client_attributes_process(client, conn, attrs);
2759 silc_attribute_payload_list_free(attrs);
2763 /* Send the attributes back */
2765 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2767 silc_command_get_ident(payload),
2768 1, 11, buffer->data, buffer->len);
2769 silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2770 NULL, 0, NULL, NULL, packet->data,
2772 silc_buffer_free(packet);
2773 silc_buffer_free(buffer);
2777 /* Client is able to receive some command packets even though they are
2778 special case. Server may send WHOIS command to the client to retrieve
2779 Requested Attributes information for WHOIS query the server is
2780 processing. This function currently handles only the WHOIS command,
2781 but if in the future more commands may arrive then this can be made
2782 to support other commands too. */
2784 SILC_FSM_STATE(silc_client_command)
2786 SilcClientConnection conn = fsm_context;
2787 SilcClient client = conn->client;
2788 SilcPacket packet = state_context;
2789 SilcCommandPayload payload;
2790 SilcCommand command;
2791 SilcArgumentPayload args;
2793 /* Get command payload from packet */
2794 payload = silc_command_payload_parse(packet->buffer.data,
2795 silc_buffer_len(&packet->buffer));
2797 SILC_LOG_DEBUG(("Bad command packet"));
2798 return SILC_FSM_FINISH;
2802 args = silc_command_get_args(payload);
2804 /* Get the command */
2805 command = silc_command_get(payload);
2808 case SILC_COMMAND_WHOIS:
2809 /* Ignore everything if requested by application */
2810 if (client->internal->params->ignore_requested_attributes)
2813 silc_client_command_process_whois(client, conn, payload, args);
2820 silc_command_payload_free(payload);
2821 return SILC_FSM_FINISH;