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 conn->internal->disconnected = TRUE;
1083 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
1085 return SILC_FSM_FINISH;
1088 /* Command QUIT. Closes connection with current server. */
1090 SILC_FSM_STATE(silc_client_command_quit)
1092 SilcClientCommandContext cmd = fsm_context;
1093 SilcClientConnection conn = cmd->conn;
1096 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1097 1, cmd->argv[1], cmd->argv_lens[1]);
1099 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1101 /* Sleep for a while */
1104 /* We close the connection with a little timeout */
1105 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1106 return SILC_FSM_WAIT;
1109 /********************************** KILL ************************************/
1112 /* Command KILL. Router operator can use this command to remove an client
1113 fromthe SILC Network. */
1115 SILC_FSM_STATE(silc_client_command_kill)
1117 SilcClientCommandContext cmd = fsm_context;
1118 SilcClientConnection conn = cmd->conn;
1119 SilcClient client = conn->client;
1120 SilcBuffer idp, auth = NULL;
1121 SilcClientEntry target;
1123 char *nickname = NULL, *comment = NULL;
1125 if (cmd->argc < 2) {
1126 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1127 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1128 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1129 return SILC_FSM_FINISH;
1132 /* Parse the typed nickname. */
1133 if (client->internal->params->nickname_parse)
1134 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
1136 nickname = strdup(cmd->argv[1]);
1138 return SILC_FSM_FINISH;
1140 /* Get the target client */
1141 clients = silc_client_get_clients_local(client, conn, nickname,
1144 /* Resolve client information */
1145 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
1147 silc_client_command_resolve_continue,
1150 target = silc_dlist_get(clients);
1152 if (cmd->argc >= 3) {
1153 if (strcasecmp(cmd->argv[2], "-pubkey"))
1154 comment = cmd->argv[2];
1156 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1157 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1158 /* Encode the public key authentication payload */
1159 auth = silc_auth_public_key_auth_generate(conn->public_key,
1162 conn->internal->sha1hash,
1163 &target->id, SILC_ID_CLIENT);
1167 /* Send the KILL command to the server */
1168 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1169 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1170 1, silc_buffer_datalen(idp),
1171 2, comment, comment ? strlen(comment) : 0,
1172 3, silc_buffer_datalen(auth));
1173 silc_buffer_free(idp);
1174 silc_buffer_free(auth);
1175 silc_free(nickname);
1176 silc_client_list_free(client, conn, clients);
1178 /* Notify application */
1179 COMMAND(SILC_STATUS_OK);
1181 /** Wait for command reply */
1182 silc_fsm_next(fsm, silc_client_command_reply_wait);
1183 return SILC_FSM_CONTINUE;
1186 /********************************** INFO ************************************/
1188 /* Command INFO. Request information about specific server. If specific
1189 server is not provided the current server is used. */
1191 SILC_FSM_STATE(silc_client_command_info)
1193 SilcClientCommandContext cmd = fsm_context;
1194 SilcClientConnection conn = cmd->conn;
1196 /* Send the command */
1198 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1199 1, cmd->argv[1], cmd->argv_lens[1]);
1201 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1203 /* Notify application */
1204 COMMAND(SILC_STATUS_OK);
1206 /** Wait for command reply */
1207 silc_fsm_next(fsm, silc_client_command_reply_wait);
1208 return SILC_FSM_CONTINUE;
1211 /********************************** STATS ***********************************/
1213 /* Command STATS. Shows server and network statistics. */
1215 SILC_FSM_STATE(silc_client_command_stats)
1217 SilcClientCommandContext cmd = fsm_context;
1218 SilcClientConnection conn = cmd->conn;
1220 /* Send the command */
1221 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1222 1, silc_buffer_datalen(conn->internal->
1225 /* Notify application */
1226 COMMAND(SILC_STATUS_OK);
1228 /** Wait for command reply */
1229 silc_fsm_next(fsm, silc_client_command_reply_wait);
1230 return SILC_FSM_CONTINUE;
1233 /********************************** PING ************************************/
1235 /* Command PING. Sends ping to server. */
1237 SILC_FSM_STATE(silc_client_command_ping)
1239 SilcClientCommandContext cmd = fsm_context;
1240 SilcClientConnection conn = cmd->conn;
1242 if (cmd->argc < 2) {
1243 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1244 return SILC_FSM_FINISH;
1247 /* Send the command */
1248 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1249 1, silc_buffer_datalen(conn->internal->
1252 /* Save ping time */
1253 cmd->context = SILC_64_TO_PTR(silc_time());
1255 /* Notify application */
1256 COMMAND(SILC_STATUS_OK);
1258 /** Wait for command reply */
1259 silc_fsm_next(fsm, silc_client_command_reply_wait);
1260 return SILC_FSM_CONTINUE;
1263 /********************************** JOIN ************************************/
1265 /* Command JOIN. Joins to a channel. */
1267 SILC_FSM_STATE(silc_client_command_join)
1269 SilcClientCommandContext cmd = fsm_context;
1270 SilcClientConnection conn = cmd->conn;
1271 SilcChannelEntry channel;
1272 SilcBuffer auth = NULL, cauth = NULL;
1273 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1274 int i, passphrase_len = 0;
1276 if (cmd->argc < 2) {
1277 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1281 /* See if we have joined to the requested channel already */
1282 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1283 if (channel && silc_client_on_channel(channel, conn->local_entry))
1286 if (cmd->argv_lens[1] > 256)
1287 cmd->argv_lens[1] = 256;
1289 name = cmd->argv[1];
1291 for (i = 2; i < cmd->argc; i++) {
1292 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1293 cipher = cmd->argv[++i];
1294 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1295 hmac = cmd->argv[++i];
1296 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1297 auth = silc_auth_public_key_auth_generate(conn->public_key,
1300 conn->internal->sha1hash,
1303 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1304 SilcPublicKey pubkey = conn->public_key;
1305 SilcPrivateKey privkey = conn->private_key;
1306 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1309 if (cmd->argc >= i + 3) {
1311 if (cmd->argc >= i + 4) {
1312 pass = cmd->argv[i + 3];
1315 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1316 &pubkey, &privkey)) {
1317 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1318 "Could not load key pair, check your arguments");
1319 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1325 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1326 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1328 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1329 memcpy(pubdata, pkhash, 20);
1330 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1332 conn->internal->sha1hash,
1335 memset(pubdata, 0, 128);
1338 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1339 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1340 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1341 cmd->argv_lens[i], 0);
1342 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1343 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1344 0, pu8, passphrase_len);
1347 passphrase = strdup(cmd->argv[i]);
1348 passphrase_len = cmd->argv_lens[i];
1353 /* Send JOIN command to the server */
1354 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1355 1, name, strlen(name),
1356 2, silc_buffer_datalen(conn->internal->
1358 3, passphrase, passphrase_len,
1359 4, cipher, cipher ? strlen(cipher) : 0,
1360 5, hmac, hmac ? strlen(hmac) : 0,
1361 6, silc_buffer_datalen(auth),
1362 7, silc_buffer_datalen(cauth));
1364 silc_buffer_free(auth);
1365 silc_buffer_free(cauth);
1367 memset(passphrase, 0, strlen(passphrase));
1368 silc_free(passphrase);
1370 /* Notify application */
1371 COMMAND(SILC_STATUS_OK);
1373 /** Wait for command reply */
1374 silc_fsm_next(fsm, silc_client_command_reply_wait);
1375 return SILC_FSM_CONTINUE;
1378 return SILC_FSM_FINISH;
1381 /********************************** MOTD ************************************/
1383 /* MOTD command. Requests motd from server. */
1385 SILC_FSM_STATE(silc_client_command_motd)
1387 SilcClientCommandContext cmd = fsm_context;
1388 SilcClientConnection conn = cmd->conn;
1390 if (cmd->argc < 1 || cmd->argc > 2) {
1391 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1392 "Usage: /MOTD [<server>]");
1393 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1394 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1395 return SILC_FSM_FINISH;
1398 /* Send the command */
1400 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1401 1, conn->remote_host,
1402 strlen(conn->remote_host));
1404 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1405 1, cmd->argv[1], cmd->argv_lens[1]);
1407 /* Notify application */
1408 COMMAND(SILC_STATUS_OK);
1410 /** Wait for command reply */
1411 silc_fsm_next(fsm, silc_client_command_reply_wait);
1412 return SILC_FSM_CONTINUE;
1415 /********************************** UMODE ***********************************/
1417 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1418 modes as client cannot set itself server/router operator privileges. */
1420 SILC_FSM_STATE(silc_client_command_umode)
1422 SilcClientCommandContext cmd = fsm_context;
1423 SilcClientConnection conn = cmd->conn;
1424 unsigned char *cp, modebuf[4];
1425 SilcUInt32 mode, add, len;
1428 if (cmd->argc < 2) {
1429 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1430 "Usage: /UMODE +|-<modes>");
1431 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1432 return SILC_FSM_FINISH;
1435 mode = conn->local_entry->mode;
1437 /* Are we adding or removing mode */
1438 if (cmd->argv[1][0] == '-')
1444 cp = cmd->argv[1] + 1;
1446 for (i = 0; i < len; i++) {
1451 mode |= SILC_UMODE_SERVER_OPERATOR;
1452 mode |= SILC_UMODE_ROUTER_OPERATOR;
1453 mode |= SILC_UMODE_GONE;
1454 mode |= SILC_UMODE_INDISPOSED;
1455 mode |= SILC_UMODE_BUSY;
1456 mode |= SILC_UMODE_PAGE;
1457 mode |= SILC_UMODE_HYPER;
1458 mode |= SILC_UMODE_ROBOT;
1459 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1460 mode |= SILC_UMODE_REJECT_WATCHING;
1462 mode = SILC_UMODE_NONE;
1467 mode |= SILC_UMODE_SERVER_OPERATOR;
1469 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1473 mode |= SILC_UMODE_ROUTER_OPERATOR;
1475 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1479 mode |= SILC_UMODE_GONE;
1481 mode &= ~SILC_UMODE_GONE;
1485 mode |= SILC_UMODE_INDISPOSED;
1487 mode &= ~SILC_UMODE_INDISPOSED;
1491 mode |= SILC_UMODE_BUSY;
1493 mode &= ~SILC_UMODE_BUSY;
1497 mode |= SILC_UMODE_PAGE;
1499 mode &= ~SILC_UMODE_PAGE;
1503 mode |= SILC_UMODE_HYPER;
1505 mode &= ~SILC_UMODE_HYPER;
1509 mode |= SILC_UMODE_ROBOT;
1511 mode &= ~SILC_UMODE_ROBOT;
1515 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1517 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1521 mode |= SILC_UMODE_REJECT_WATCHING;
1523 mode &= ~SILC_UMODE_REJECT_WATCHING;
1527 mode |= SILC_UMODE_BLOCK_INVITE;
1529 mode &= ~SILC_UMODE_BLOCK_INVITE;
1532 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1533 return SILC_FSM_FINISH;
1538 SILC_PUT32_MSB(mode, modebuf);
1540 /* Send the command */
1541 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1542 1, silc_buffer_datalen(conn->internal->
1544 2, modebuf, sizeof(modebuf));
1546 /* Notify application */
1547 COMMAND(SILC_STATUS_OK);
1549 /** Wait for command reply */
1550 silc_fsm_next(fsm, silc_client_command_reply_wait);
1551 return SILC_FSM_CONTINUE;
1554 /********************************** CMODE ***********************************/
1556 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1557 can be set several at once. Those modes that require argument must be set
1558 separately (unless set with modes that does not require arguments). */
1560 SILC_FSM_STATE(silc_client_command_cmode)
1562 SilcClientCommandContext cmd = fsm_context;
1563 SilcClientConnection conn = cmd->conn;
1564 SilcClient client = conn->client;
1565 SilcChannelEntry channel;
1566 SilcBuffer chidp, auth = NULL, pk = NULL;
1567 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1568 SilcUInt32 mode, add, type, len, arg_len = 0;
1571 if (cmd->argc < 3) {
1572 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1573 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1574 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1578 if (cmd->argv[1][0] == '*') {
1579 if (!conn->current_channel) {
1580 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1584 channel = conn->current_channel;
1586 name = cmd->argv[1];
1588 channel = silc_client_get_channel(conn->client, conn, name);
1590 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1595 mode = channel->mode;
1597 /* Are we adding or removing mode */
1598 if (cmd->argv[2][0] == '-')
1603 /* Argument type to be sent to server */
1607 cp = cmd->argv[2] + 1;
1609 for (i = 0; i < len; i++) {
1613 mode |= SILC_CHANNEL_MODE_PRIVATE;
1615 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1619 mode |= SILC_CHANNEL_MODE_SECRET;
1621 mode &= ~SILC_CHANNEL_MODE_SECRET;
1625 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1627 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1631 mode |= SILC_CHANNEL_MODE_INVITE;
1633 mode &= ~SILC_CHANNEL_MODE_INVITE;
1637 mode |= SILC_CHANNEL_MODE_TOPIC;
1639 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1643 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1645 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1649 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1651 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1656 mode |= SILC_CHANNEL_MODE_ULIMIT;
1658 if (cmd->argc < 4) {
1659 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1660 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1661 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1664 ll = atoi(cmd->argv[3]);
1665 SILC_PUT32_MSB(ll, tmp);
1669 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1674 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1676 if (cmd->argc < 4) {
1677 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1678 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1679 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1683 arg_len = cmd->argv_lens[3];
1685 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1690 mode |= SILC_CHANNEL_MODE_CIPHER;
1692 if (cmd->argc < 4) {
1693 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1694 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1695 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1699 arg_len = cmd->argv_lens[3];
1701 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1706 mode |= SILC_CHANNEL_MODE_HMAC;
1708 if (cmd->argc < 4) {
1709 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1710 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1711 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1715 arg_len = cmd->argv_lens[3];
1717 mode &= ~SILC_CHANNEL_MODE_HMAC;
1722 SilcPublicKey pubkey = conn->public_key;
1723 SilcPrivateKey privkey = conn->private_key;
1725 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1728 if (cmd->argc >= 5) {
1731 pass = cmd->argv[5];
1732 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1733 &pubkey, &privkey)) {
1734 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1735 "Could not load key pair, check your arguments");
1736 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1741 pk = silc_public_key_payload_encode(pubkey);
1742 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1744 conn->internal->sha1hash,
1747 arg = silc_buffer_data(auth);
1748 arg_len = silc_buffer_len(auth);
1750 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1756 SilcBool chadd = FALSE;
1757 SilcPublicKey chpk = NULL;
1759 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1762 if (cmd->argc == 3) {
1763 /* Send empty command to receive the public key list. */
1764 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1765 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1767 1, silc_buffer_datalen(chidp));
1768 silc_buffer_free(chidp);
1770 /* Notify application */
1771 COMMAND(SILC_STATUS_OK);
1775 if (cmd->argc >= 4) {
1776 auth = silc_buffer_alloc_size(2);
1777 silc_buffer_format(auth,
1778 SILC_STR_UI_SHORT(cmd->argc - 3),
1782 for (k = 3; k < cmd->argc; k++) {
1783 if (cmd->argv[k][0] == '+')
1785 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1786 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1787 "Could not load public key %s, check the filename",
1789 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1790 silc_buffer_free(auth);
1795 pk = silc_public_key_payload_encode(chpk);
1796 auth = silc_argument_payload_encode_one(auth,
1797 silc_buffer_datalen(pk),
1798 chadd ? 0x00 : 0x01);
1799 silc_pkcs_public_key_free(chpk);
1800 silc_buffer_free(pk);
1805 arg = silc_buffer_data(auth);
1806 arg_len = silc_buffer_len(auth);
1808 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1812 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1818 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1819 SILC_PUT32_MSB(mode, modebuf);
1821 /* Send the command. We support sending only one mode at once that
1822 requires an argument. */
1824 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1825 1, silc_buffer_datalen(chidp),
1826 2, modebuf, sizeof(modebuf),
1828 8, silc_buffer_datalen(pk));
1830 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1831 1, silc_buffer_datalen(chidp),
1832 2, modebuf, sizeof(modebuf));
1835 silc_buffer_free(chidp);
1836 silc_buffer_free(auth);
1837 silc_buffer_free(pk);
1839 /* Notify application */
1840 COMMAND(SILC_STATUS_OK);
1842 /** Wait for command reply */
1843 silc_fsm_next(fsm, silc_client_command_reply_wait);
1844 return SILC_FSM_CONTINUE;
1847 return SILC_FSM_FINISH;
1850 /********************************* CUMODE ***********************************/
1852 /* CUMODE command. Changes client's mode on a channel. */
1854 SILC_FSM_STATE(silc_client_command_cumode)
1856 SilcClientCommandContext cmd = fsm_context;
1857 SilcClientConnection conn = cmd->conn;
1858 SilcClient client = conn->client;
1859 SilcChannelEntry channel;
1860 SilcChannelUser chu;
1861 SilcClientEntry client_entry;
1862 SilcBuffer clidp, chidp, auth = NULL;
1863 SilcDList clients = NULL;
1864 unsigned char *name, *cp, modebuf[4];
1865 SilcUInt32 mode = 0, add, len;
1866 char *nickname = NULL;
1869 if (cmd->argc < 4) {
1870 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1871 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1872 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1876 if (cmd->argv[1][0] == '*') {
1877 if (!conn->current_channel) {
1878 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1882 channel = conn->current_channel;
1884 name = cmd->argv[1];
1886 channel = silc_client_get_channel(conn->client, conn, name);
1888 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1893 /* Parse the typed nickname. */
1894 if (client->internal->params->nickname_parse)
1895 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1897 nickname = strdup(cmd->argv[3]);
1899 /* Find client entry */
1900 clients = silc_client_get_clients_local(client, conn, nickname,
1903 /* Resolve client information */
1904 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1905 silc_client_command_resolve_continue,
1908 client_entry = silc_dlist_get(clients);
1910 /* Get the current mode */
1911 chu = silc_client_on_channel(channel, client_entry);
1915 /* Are we adding or removing mode */
1916 if (cmd->argv[2][0] == '-')
1922 cp = cmd->argv[2] + 1;
1924 for (i = 0; i < len; i++) {
1928 mode |= SILC_CHANNEL_UMODE_CHANFO;
1929 mode |= SILC_CHANNEL_UMODE_CHANOP;
1930 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1931 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1932 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1934 mode = SILC_CHANNEL_UMODE_NONE;
1939 SilcPublicKey pubkey = conn->public_key;
1940 SilcPrivateKey privkey = conn->private_key;
1942 if (cmd->argc >= 6) {
1945 pass = cmd->argv[6];
1946 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1947 &pubkey, &privkey)) {
1948 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1949 "Could not load key pair, check your arguments");
1950 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1955 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1957 conn->internal->sha1hash,
1960 mode |= SILC_CHANNEL_UMODE_CHANFO;
1962 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1967 mode |= SILC_CHANNEL_UMODE_CHANOP;
1969 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1973 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1975 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1979 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1981 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1985 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1987 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1991 mode |= SILC_CHANNEL_UMODE_QUIET;
1993 mode &= ~SILC_CHANNEL_UMODE_QUIET;
1996 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2002 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2003 SILC_PUT32_MSB(mode, modebuf);
2004 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2006 /* Send the command packet. We support sending only one mode at once
2007 that requires an argument. */
2008 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2009 1, silc_buffer_datalen(chidp),
2011 3, silc_buffer_datalen(clidp),
2012 4, silc_buffer_datalen(auth));
2014 silc_buffer_free(chidp);
2015 silc_buffer_free(clidp);
2017 silc_buffer_free(auth);
2018 silc_free(nickname);
2019 silc_client_list_free(client, conn, clients);
2021 /* Notify application */
2022 COMMAND(SILC_STATUS_OK);
2024 /** Wait for command reply */
2025 silc_fsm_next(fsm, silc_client_command_reply_wait);
2026 return SILC_FSM_CONTINUE;
2029 silc_client_list_free(client, conn, clients);
2030 silc_free(nickname);
2031 return SILC_FSM_FINISH;
2034 /********************************** KICK ************************************/
2036 /* KICK command. Kicks a client out of channel. */
2038 SILC_FSM_STATE(silc_client_command_kick)
2040 SilcClientCommandContext cmd = fsm_context;
2041 SilcClientConnection conn = cmd->conn;
2042 SilcClient client = conn->client;
2043 SilcChannelEntry channel;
2044 SilcBuffer idp, idp2;
2045 SilcClientEntry target;
2046 SilcDList clients = NULL;
2048 char *nickname = NULL;
2050 if (cmd->argc < 3) {
2051 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2052 "Usage: /KICK <channel> <nickname> [<comment>]");
2053 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2057 if (cmd->argv[1][0] == '*') {
2058 if (!conn->current_channel) {
2059 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2062 name = conn->current_channel->channel_name;
2064 name = cmd->argv[1];
2067 if (!conn->current_channel) {
2068 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2072 /* Get the Channel ID of the channel */
2073 channel = silc_client_get_channel(conn->client, conn, name);
2075 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2079 /* Parse the typed nickname. */
2080 if (client->internal->params->nickname_parse)
2081 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
2083 nickname = strdup(cmd->argv[2]);
2085 /* Get the target client */
2086 clients = silc_client_get_clients_local(client, conn, nickname,
2089 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2090 "No such client: %s", cmd->argv[2]);
2091 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2094 target = silc_dlist_get(clients);
2096 /* Send KICK command to the server */
2097 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2098 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2100 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2101 1, silc_buffer_datalen(idp),
2102 2, silc_buffer_datalen(idp2));
2104 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2105 1, silc_buffer_datalen(idp),
2106 2, silc_buffer_datalen(idp2),
2107 3, cmd->argv[3], strlen(cmd->argv[3]));
2109 silc_buffer_free(idp);
2110 silc_buffer_free(idp2);
2111 silc_free(nickname);
2112 silc_client_list_free(client, conn, clients);
2114 /* Notify application */
2115 COMMAND(SILC_STATUS_OK);
2117 /** Wait for command reply */
2118 silc_fsm_next(fsm, silc_client_command_reply_wait);
2119 return SILC_FSM_CONTINUE;
2122 silc_free(nickname);
2123 return SILC_FSM_FINISH;
2126 /***************************** OPER & SILCOPER ******************************/
2129 unsigned char *passphrase;
2130 SilcUInt32 passphrase_len;
2131 } *SilcClientCommandOper;
2133 /* Ask passphrase callback */
2135 static void silc_client_command_oper_cb(unsigned char *data,
2136 SilcUInt32 data_len, void *context)
2138 SilcClientCommandContext cmd = context;
2139 SilcClientCommandOper oper = cmd->context;
2141 if (data && data_len)
2142 oper->passphrase = silc_memdup(data, data_len);
2143 oper->passphrase_len = data_len;
2146 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2149 /* Send OPER/SILCOPER command */
2151 SILC_FSM_STATE(silc_client_command_oper_send)
2153 SilcClientCommandContext cmd = fsm_context;
2154 SilcClientConnection conn = cmd->conn;
2155 SilcClientCommandOper oper = cmd->context;
2158 if (!oper || !oper->passphrase) {
2159 /* Encode the public key authentication payload */
2160 auth = silc_auth_public_key_auth_generate(conn->public_key,
2163 conn->internal->hash,
2167 /* Encode the password authentication payload */
2168 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2169 oper->passphrase, oper->passphrase_len);
2172 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2173 1, cmd->argv[1], strlen(cmd->argv[1]),
2174 2, silc_buffer_datalen(auth));
2176 silc_buffer_clear(auth);
2177 silc_buffer_free(auth);
2179 silc_free(oper->passphrase);
2183 /* Notify application */
2184 COMMAND(SILC_STATUS_OK);
2186 /** Wait for command reply */
2187 silc_fsm_next(fsm, silc_client_command_reply_wait);
2188 return SILC_FSM_CONTINUE;
2191 /* OPER command. Used to obtain server operator privileges. */
2193 SILC_FSM_STATE(silc_client_command_oper)
2195 SilcClientCommandContext cmd = fsm_context;
2196 SilcClientConnection conn = cmd->conn;
2197 SilcClientCommandOper oper;
2199 if (cmd->argc < 2) {
2200 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2201 "Usage: /OPER <username> [-pubkey]");
2202 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2203 return SILC_FSM_FINISH;
2206 /* Get passphrase */
2207 if (cmd->argc < 3) {
2208 oper = silc_calloc(1, sizeof(*oper));
2210 return SILC_FSM_FINISH;
2211 cmd->context = oper;
2212 SILC_FSM_CALL(conn->client->internal->
2213 ops->ask_passphrase(conn->client, conn,
2214 silc_client_command_oper_cb, cmd));
2217 silc_fsm_next(fsm, silc_client_command_oper_send);
2218 return SILC_FSM_CONTINUE;
2221 /* SILCOPER command. Used to obtain router operator privileges. */
2223 SILC_FSM_STATE(silc_client_command_silcoper)
2225 SilcClientCommandContext cmd = fsm_context;
2226 SilcClientConnection conn = cmd->conn;
2227 SilcClientCommandOper oper;
2229 if (cmd->argc < 2) {
2230 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2231 "Usage: /SILCOPER <username> [-pubkey]");
2232 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2233 return SILC_FSM_FINISH;
2236 /* Get passphrase */
2237 if (cmd->argc < 3) {
2238 oper = silc_calloc(1, sizeof(*oper));
2240 return SILC_FSM_FINISH;
2241 cmd->context = oper;
2242 SILC_FSM_CALL(conn->client->internal->
2243 ops->ask_passphrase(conn->client, conn,
2244 silc_client_command_oper_cb, cmd));
2247 silc_fsm_next(fsm, silc_client_command_oper_send);
2248 return SILC_FSM_CONTINUE;
2251 /*********************************** BAN ************************************/
2253 /* Command BAN. This is used to manage the ban list of the channel. */
2255 SILC_FSM_STATE(silc_client_command_ban)
2257 SilcClientCommandContext cmd = fsm_context;
2258 SilcClientConnection conn = cmd->conn;
2259 SilcChannelEntry channel;
2260 SilcBuffer chidp, args = NULL;
2261 char *name, *ban = NULL;
2262 unsigned char action[1];
2263 SilcPublicKey pubkey = NULL;
2265 if (cmd->argc < 2) {
2266 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2267 "Usage: /BAN <channel> "
2268 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2269 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2273 if (cmd->argv[1][0] == '*') {
2274 if (!conn->current_channel) {
2275 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2279 channel = conn->current_channel;
2281 name = cmd->argv[1];
2283 channel = silc_client_get_channel(conn->client, conn, name);
2285 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2290 if (cmd->argc == 3) {
2291 if (cmd->argv[2][0] == '+')
2296 /* Check if it is public key file to be added to invite list */
2297 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2304 args = silc_buffer_alloc_size(2);
2305 silc_buffer_format(args,
2306 SILC_STR_UI_SHORT(1),
2309 chidp = silc_public_key_payload_encode(pubkey);
2310 args = silc_argument_payload_encode_one(args,
2311 silc_buffer_datalen(chidp), 2);
2312 silc_buffer_free(chidp);
2313 silc_pkcs_public_key_free(pubkey);
2315 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2319 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2321 /* Send the command */
2322 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2323 1, silc_buffer_datalen(chidp),
2324 2, args ? action : NULL, args ? 1 : 0,
2325 3, silc_buffer_datalen(args));
2327 silc_buffer_free(chidp);
2328 silc_buffer_free(args);
2330 /* Notify application */
2331 COMMAND(SILC_STATUS_OK);
2333 /** Wait for command reply */
2334 silc_fsm_next(fsm, silc_client_command_reply_wait);
2335 return SILC_FSM_CONTINUE;
2338 return SILC_FSM_FINISH;
2341 /********************************* DETACH ***********************************/
2343 /* Command DETACH. This is used to detach from the server */
2345 SILC_FSM_STATE(silc_client_command_detach)
2347 SilcClientCommandContext cmd = fsm_context;
2348 SilcClientConnection conn = cmd->conn;
2350 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2352 /* Notify application */
2353 COMMAND(SILC_STATUS_OK);
2355 /** Wait for command reply */
2356 silc_fsm_next(fsm, silc_client_command_reply_wait);
2357 return SILC_FSM_CONTINUE;
2360 /********************************** WATCH ***********************************/
2362 /* Command WATCH. */
2364 SILC_FSM_STATE(silc_client_command_watch)
2366 SilcClientCommandContext cmd = fsm_context;
2367 SilcClientConnection conn = cmd->conn;
2368 SilcBuffer args = NULL;
2370 const char *pubkey = NULL;
2371 SilcBool pubkey_add = TRUE;
2373 if (cmd->argc < 3) {
2374 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2378 if (!strcasecmp(cmd->argv[1], "-add")) {
2380 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2382 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2384 pubkey = cmd->argv[2] + 1;
2385 if (cmd->argv[2][0] == '-')
2388 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2396 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2397 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2398 "Could not load public key %s, check the filename", pubkey);
2399 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2403 args = silc_buffer_alloc_size(2);
2404 silc_buffer_format(args,
2405 SILC_STR_UI_SHORT(1),
2407 buffer = silc_public_key_payload_encode(pk);
2408 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2409 pubkey_add ? 0x00 : 0x01);
2410 silc_buffer_free(buffer);
2411 silc_pkcs_public_key_free(pk);
2414 /* Send the commmand */
2415 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2416 1, silc_buffer_datalen(conn->internal->
2418 type, pubkey ? args->data : cmd->argv[2],
2419 pubkey ? silc_buffer_len(args) :
2422 silc_buffer_free(args);
2424 /* Notify application */
2425 COMMAND(SILC_STATUS_OK);
2427 /** Wait for command reply */
2428 silc_fsm_next(fsm, silc_client_command_reply_wait);
2429 return SILC_FSM_CONTINUE;
2432 return SILC_FSM_FINISH;
2435 /********************************** LEAVE ***********************************/
2437 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2439 SILC_FSM_STATE(silc_client_command_leave)
2441 SilcClientCommandContext cmd = fsm_context;
2442 SilcClientConnection conn = cmd->conn;
2443 SilcChannelEntry channel;
2447 if (cmd->argc != 2) {
2448 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2449 "Usage: /LEAVE <channel>");
2450 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2454 if (cmd->argv[1][0] == '*') {
2455 if (!conn->current_channel) {
2456 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2459 name = conn->current_channel->channel_name;
2461 name = cmd->argv[1];
2464 /* Get the channel entry */
2465 channel = silc_client_get_channel(conn->client, conn, name);
2467 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2471 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2473 /* Send LEAVE command to the server */
2474 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2475 1, silc_buffer_datalen(idp));
2477 silc_buffer_free(idp);
2479 /* Notify application */
2480 COMMAND(SILC_STATUS_OK);
2482 if (conn->current_channel == channel)
2483 conn->current_channel = NULL;
2485 /** Wait for command reply */
2486 silc_fsm_next(fsm, silc_client_command_reply_wait);
2487 return SILC_FSM_CONTINUE;
2490 return SILC_FSM_FINISH;
2493 /********************************** USERS ***********************************/
2495 /* Command USERS. Requests the USERS of the clients joined on requested
2498 SILC_FSM_STATE(silc_client_command_users)
2500 SilcClientCommandContext cmd = fsm_context;
2501 SilcClientConnection conn = cmd->conn;
2504 if (cmd->argc != 2) {
2505 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2506 "Usage: /USERS <channel>");
2507 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2511 if (cmd->argv[1][0] == '*') {
2512 if (!conn->current_channel) {
2513 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2516 name = conn->current_channel->channel_name;
2518 name = cmd->argv[1];
2521 /* Send USERS command to the server */
2522 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2523 2, name, strlen(name));
2525 /* Notify application */
2526 COMMAND(SILC_STATUS_OK);
2528 /** Wait for command reply */
2529 silc_fsm_next(fsm, silc_client_command_reply_wait);
2530 return SILC_FSM_CONTINUE;
2533 return SILC_FSM_FINISH;
2536 /********************************* GETKEY ***********************************/
2538 /* Command GETKEY. Used to fetch remote client's public key. */
2540 SILC_FSM_STATE(silc_client_command_getkey)
2542 SilcClientCommandContext cmd = fsm_context;
2543 SilcClientConnection conn = cmd->conn;
2544 SilcClient client = conn->client;
2545 SilcClientEntry client_entry;
2546 SilcServerEntry server_entry;
2548 char *nickname = NULL;
2551 if (cmd->argc < 2) {
2552 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2553 "Usage: /GETKEY <nickname or server name>");
2554 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2555 return SILC_FSM_FINISH;
2558 /* Parse the typed nickname. */
2559 if (client->internal->params->nickname_parse)
2560 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2562 nickname = strdup(cmd->argv[1]);
2564 COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2565 return SILC_FSM_FINISH;
2568 /* Find client entry */
2569 clients = silc_client_get_clients_local(client, conn, nickname,
2572 /* Check whether user requested server */
2573 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2574 if (!server_entry) {
2575 if (cmd->resolved) {
2576 /* Resolving didn't find anything. We should never get here as
2577 errors are handled in the resolving callback. */
2578 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2579 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2580 return SILC_FSM_FINISH;
2583 /* No client or server exist with this name, query for both. */
2584 cmd->resolved = TRUE;
2585 SILC_FSM_CALL(silc_client_command_send(client, conn,
2586 SILC_COMMAND_IDENTIFY,
2587 silc_client_command_continue,
2590 strlen(cmd->argv[1]),
2592 strlen(cmd->argv[1])));
2595 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2596 silc_client_unref_server(client, conn, server_entry);
2598 client_entry = silc_dlist_get(clients);
2599 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2600 silc_client_list_free(client, conn, clients);
2603 /* Send the commmand */
2604 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2605 1, silc_buffer_datalen(idp));
2607 silc_buffer_free(idp);
2608 silc_free(nickname);
2610 /* Notify application */
2611 COMMAND(SILC_STATUS_OK);
2613 /** Wait for command reply */
2614 silc_fsm_next(fsm, silc_client_command_reply_wait);
2615 return SILC_FSM_CONTINUE;
2618 /********************************* SERVICE **********************************/
2620 /* Command SERVICE. Negotiates service agreement with server. */
2621 /* XXX incomplete */
2623 SILC_FSM_STATE(silc_client_command_service)
2625 SilcClientCommandContext cmd = fsm_context;
2627 SilcClientConnection conn = cmd->conn;
2631 if (cmd->argc < 2) {
2632 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2633 "Usage: /SERVICE [<service name>] [-pubkey]");
2634 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2635 return SILC_FSM_FINISH;
2638 name = cmd->argv[1];
2640 /* Send SERVICE command to the server */
2641 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2642 ++conn->cmd_ident, 1,
2643 1, name, strlen(name));
2644 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2645 NULL, 0, NULL, NULL, buffer->data,
2647 silc_buffer_free(buffer);
2650 /* Notify application */
2651 COMMAND(SILC_STATUS_OK);
2653 /** Wait for command reply */
2654 silc_fsm_next(fsm, silc_client_command_reply_wait);
2655 return SILC_FSM_CONTINUE;
2658 /* Register all default commands provided by the client library for the
2661 void silc_client_commands_register(SilcClient client)
2663 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2666 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2667 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2668 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2669 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2670 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2671 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2672 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2673 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2674 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2675 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2676 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2677 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2678 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2679 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2680 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2681 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2682 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2683 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2684 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2685 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2686 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2687 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2688 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2689 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2690 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2691 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2692 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2695 /* Unregister all commands. */
2697 void silc_client_commands_unregister(SilcClient client)
2699 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2700 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2701 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2702 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2703 SILC_CLIENT_CMDU(list, LIST, "LIST");
2704 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2705 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2706 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2707 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2708 SILC_CLIENT_CMDU(info, INFO, "INFO");
2709 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2710 SILC_CLIENT_CMDU(ping, PING, "PING");
2711 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2712 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2713 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2714 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2715 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2716 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2717 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2718 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2719 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2720 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2721 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2722 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2723 SILC_CLIENT_CMDU(users, USERS, "USERS");
2724 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2725 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2728 /****************** Client Side Incoming Command Handling *******************/
2730 /* Reply to WHOIS command from server */
2732 static void silc_client_command_process_whois(SilcClient client,
2733 SilcClientConnection conn,
2734 SilcCommandPayload payload,
2735 SilcArgumentPayload args)
2741 SilcBuffer buffer, packet;
2743 SILC_LOG_DEBUG(("Received WHOIS command"));
2745 /* Try to take the Requested Attributes */
2746 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2750 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2754 /* Process requested attributes */
2755 buffer = silc_client_attributes_process(client, conn, attrs);
2757 silc_attribute_payload_list_free(attrs);
2761 /* Send the attributes back */
2763 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2765 silc_command_get_ident(payload),
2766 1, 11, buffer->data, buffer->len);
2767 silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2768 NULL, 0, NULL, NULL, packet->data,
2770 silc_buffer_free(packet);
2771 silc_buffer_free(buffer);
2775 /* Client is able to receive some command packets even though they are
2776 special case. Server may send WHOIS command to the client to retrieve
2777 Requested Attributes information for WHOIS query the server is
2778 processing. This function currently handles only the WHOIS command,
2779 but if in the future more commands may arrive then this can be made
2780 to support other commands too. */
2782 SILC_FSM_STATE(silc_client_command)
2784 SilcClientConnection conn = fsm_context;
2785 SilcClient client = conn->client;
2786 SilcPacket packet = state_context;
2787 SilcCommandPayload payload;
2788 SilcCommand command;
2789 SilcArgumentPayload args;
2791 /* Get command payload from packet */
2792 payload = silc_command_payload_parse(packet->buffer.data,
2793 silc_buffer_len(&packet->buffer));
2795 SILC_LOG_DEBUG(("Bad command packet"));
2796 return SILC_FSM_FINISH;
2800 args = silc_command_get_args(payload);
2802 /* Get the command */
2803 command = silc_command_get(payload);
2806 case SILC_COMMAND_WHOIS:
2807 /* Ignore everything if requested by application */
2808 if (client->internal->params->ignore_requested_attributes)
2811 silc_client_command_process_whois(client, conn, payload, args);
2818 silc_command_payload_free(payload);
2819 return SILC_FSM_FINISH;