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 /* If command is running, finish it. Destructor will free the context. */
356 if (silc_fsm_is_started(&cmd->thread)) {
357 silc_fsm_finish(&cmd->thread);
361 for (i = 0; i < cmd->argc; i++)
362 silc_free(cmd->argv[i]);
363 silc_free(cmd->argv);
364 silc_free(cmd->argv_lens);
365 silc_free(cmd->argv_types);
367 silc_list_start(cmd->reply_callbacks);
368 while ((cb = silc_list_get(cmd->reply_callbacks)))
374 /* Executes a command */
376 SilcUInt16 silc_client_command_call(SilcClient client,
377 SilcClientConnection conn,
378 const char *command_line, ...)
382 unsigned char **argv = NULL;
383 SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
384 SilcClientCommand command;
385 SilcClientCommandContext cmd;
389 client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_ERROR,
390 "You are not connected to a server, please connect to server");
394 /* Parse arguments */
395 va_start(va, command_line);
399 /* Get command name */
400 command_name = silc_memdup(command_line, strcspn(command_line, " "));
404 /* Find command by name */
405 command = silc_client_command_find(client, command_name);
407 silc_free(command_name);
411 /* Parse command line */
412 silc_parse_command_line((char *)command_line, &argv, &argv_lens,
413 &argv_types, &argc, command->max_args);
415 silc_free(command_name);
417 arg = va_arg(va, char *);
421 /* Find command by name */
422 command = silc_client_command_find(client, arg);
427 argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
428 argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
429 argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
430 if (!argv || !argv_lens || !argv_types)
432 argv[argc] = silc_memdup(arg, strlen(arg));
435 argv_lens[argc] = strlen(arg);
436 argv_types[argc] = argc;
438 arg = va_arg(va, char *);
443 /* Allocate command context */
444 cmd = silc_calloc(1, sizeof(*cmd));
448 cmd->cmd = command->cmd;
451 cmd->argv_lens = argv_lens;
452 cmd->argv_types = argv_types;
453 cmd->cmd_ident = silc_client_cmd_ident(conn);
456 silc_list_init(cmd->reply_callbacks,
457 struct SilcClientCommandReplyCallbackStruct, next);
460 SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
461 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
462 silc_client_command_destructor, NULL, FALSE);
463 silc_fsm_start_sync(&cmd->thread, command->command);
465 return cmd->cmd_ident;
468 /* Generic function to send any command. The arguments must be sent already
469 encoded into correct format and in correct order. */
471 SilcUInt16 silc_client_command_send(SilcClient client,
472 SilcClientConnection conn,
474 SilcClientCommandReply reply,
476 SilcUInt32 argc, ...)
478 SilcClientCommandContext cmd;
484 /* Allocate command context */
485 cmd = silc_calloc(1, sizeof(*cmd));
490 silc_list_init(cmd->reply_callbacks,
491 struct SilcClientCommandReplyCallbackStruct, next);
493 /* Send the command */
496 silc_client_command_send_vap(client, conn, cmd, command, reply,
497 reply_context, argc, ap);
500 if (!cmd->cmd_ident) {
501 silc_client_command_free(cmd);
505 /*** Wait for command reply */
506 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
507 silc_client_command_destructor, NULL, FALSE);
508 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
510 return cmd->cmd_ident;
513 /* Generic function to send any command. The arguments must be sent already
514 encoded into correct format and in correct order. Arguments come from
517 SilcUInt16 silc_client_command_send_argv(SilcClient client,
518 SilcClientConnection conn,
520 SilcClientCommandReply reply,
523 unsigned char **argv,
524 SilcUInt32 *argv_lens,
525 SilcUInt32 *argv_types)
527 SilcClientCommandContext cmd;
532 /* Allocate command context */
533 cmd = silc_calloc(1, sizeof(*cmd));
539 /* Send the command */
541 silc_client_command_send_arg_array(client, conn, cmd, command, reply,
542 reply_context, argc, argv, argv_lens,
544 if (!cmd->cmd_ident) {
545 silc_client_command_free(cmd);
549 /*** Wait for command reply */
550 silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
551 silc_client_command_destructor, NULL, FALSE);
552 silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
554 return cmd->cmd_ident;
557 /* Attach to a command and command identifier to receive command reply. */
559 SilcBool silc_client_command_pending(SilcClientConnection conn,
562 SilcClientCommandReply reply,
565 SilcClientCommandContext cmd;
566 SilcClientCommandReplyCallback cb;
571 SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
573 silc_mutex_lock(conn->internal->lock);
575 /* Find the pending command */
576 silc_list_start(conn->internal->pending_commands);
577 while ((cmd = silc_list_get(conn->internal->pending_commands)))
578 if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
579 && cmd->cmd_ident == ident) {
581 /* Add the callback */
582 cb = silc_calloc(1, sizeof(*cb));
586 cb->context = context;
587 silc_list_add(cmd->reply_callbacks, cb);
590 silc_mutex_unlock(conn->internal->lock);
595 /******************************** WHOIS *************************************/
597 /* Command WHOIS. This command is used to query information about
600 SILC_FSM_STATE(silc_client_command_whois)
602 SilcClientCommandContext cmd = fsm_context;
603 SilcClientConnection conn = cmd->conn;
604 SilcClient client = conn->client;
605 SilcBuffer attrs = NULL;
606 unsigned char count[4], *tmp = NULL;
607 SilcBool details = FALSE, nick = FALSE;
608 unsigned char *pubkey = NULL;
611 /* Given without arguments fetches client's own information */
613 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
614 silc_buffer_data(conn->internal->local_idp),
615 silc_buffer_len(conn->internal->local_idp));
617 /* Notify application */
618 COMMAND(SILC_STATUS_OK);
620 /** Wait for command reply */
621 silc_fsm_next(fsm, silc_client_command_reply_wait);
622 return SILC_FSM_CONTINUE;
625 for (i = 1; i < cmd->argc; i++) {
626 if (!strcasecmp(cmd->argv[i], "-details")) {
628 } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
629 pubkey = cmd->argv[i + 1];
632 /* We assume that the first parameter is the nickname, if it isn't
633 -details or -pubkey. The last parameter should always be the count */
636 } else if (i == cmd->argc - 1) {
637 int c = atoi(cmd->argv[i]);
638 SILC_PUT32_MSB(c, count);
645 /* If pubkey is set, add all attributes to the attrs buffer, except
648 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
649 SILC_ATTRIBUTE_SERVICE,
650 SILC_ATTRIBUTE_STATUS_MOOD,
651 SILC_ATTRIBUTE_STATUS_FREETEXT,
652 SILC_ATTRIBUTE_STATUS_MESSAGE,
653 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
654 SILC_ATTRIBUTE_PREFERRED_CONTACT,
655 SILC_ATTRIBUTE_TIMEZONE,
656 SILC_ATTRIBUTE_GEOLOCATION,
657 SILC_ATTRIBUTE_DEVICE_INFO,
658 SILC_ATTRIBUTE_USER_ICON, 0);
660 attrs = silc_client_attributes_request(0);
665 SilcAttributeObjPk obj;
668 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
669 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
670 "Could not load public key %s, check the filename",
672 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
676 switch (silc_pkcs_get_type(pk)) {
678 obj.type = "silc-rsa";
681 obj.type = "ssh-rsa";
683 case SILC_PKCS_X509V3:
684 obj.type = "x509v3-sign-rsa";
686 case SILC_PKCS_OPENPGP:
687 obj.type = "pgp-sign-rsa";
693 obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
695 attrs = silc_attribute_payload_encode(attrs,
696 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
697 SILC_ATTRIBUTE_FLAG_VALID,
702 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
703 3, 1, nick ? cmd->argv[1] : NULL,
704 nick ? cmd->argv_lens[1] : 0,
705 2, tmp ? tmp : NULL, tmp ? 4 : 0,
706 3, silc_buffer_datalen(attrs));
708 /* Notify application */
709 COMMAND(SILC_STATUS_OK);
711 /** Wait for command reply */
712 silc_fsm_next(fsm, silc_client_command_reply_wait);
713 return SILC_FSM_CONTINUE;
716 return SILC_FSM_FINISH;
719 /******************************** WHOWAS ************************************/
721 /* Command WHOWAS. This command is used to query history information about
722 specific user that used to exist in the network. */
724 SILC_FSM_STATE(silc_client_command_whowas)
726 SilcClientCommandContext cmd = fsm_context;
727 SilcClientConnection conn = cmd->conn;
728 unsigned char count[4];
731 if (cmd->argc < 2 || cmd->argc > 3) {
732 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
733 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
734 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
735 SILC_STATUS_ERR_TOO_MANY_PARAMS));
736 return SILC_FSM_FINISH;
739 if (cmd->argc == 2) {
740 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
741 1, 1, cmd->argv[1], cmd->argv_lens[1]);
743 c = atoi(cmd->argv[2]);
744 SILC_PUT32_MSB(c, count);
745 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
746 2, 1, cmd->argv[1], cmd->argv_lens[1],
747 2, count, sizeof(count));
750 /* Notify application */
751 COMMAND(SILC_STATUS_OK);
753 /** Wait for command reply */
754 silc_fsm_next(fsm, silc_client_command_reply_wait);
755 return SILC_FSM_CONTINUE;
758 /******************************** IDENTIFY **********************************/
760 /* Command IDENTIFY. This command is used to query information about
761 specific user, especially ID's. */
763 SILC_FSM_STATE(silc_client_command_identify)
765 SilcClientCommandContext cmd = fsm_context;
766 SilcClientConnection conn = cmd->conn;
767 unsigned char count[4];
770 if (cmd->argc < 2 || cmd->argc > 3)
771 return SILC_FSM_FINISH;
773 if (cmd->argc == 2) {
774 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
775 1, 1, cmd->argv[1], cmd->argv_lens[1]);
777 c = atoi(cmd->argv[2]);
778 SILC_PUT32_MSB(c, count);
779 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
780 2, 1, cmd->argv[1], cmd->argv_lens[1],
781 4, count, sizeof(count));
784 /** Wait for command reply */
785 silc_fsm_next(fsm, silc_client_command_reply_wait);
786 return SILC_FSM_CONTINUE;
789 /********************************** NICK ************************************/
791 /* Command NICK. Shows current nickname/sets new nickname on current
794 SILC_FSM_STATE(silc_client_command_nick)
796 SilcClientCommandContext cmd = fsm_context;
797 SilcClientConnection conn = cmd->conn;
800 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
801 "Usage: /NICK <nickname>");
802 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
806 if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
809 /* Show current nickname */
812 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
813 "Your nickname is %s on server %s",
814 conn->local_entry->nickname, conn->remote_host);
816 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
817 "Your nickname is %s", conn->local_entry->nickname);
820 COMMAND(SILC_STATUS_OK);
824 if (cmd->argv_lens[1] > 128)
825 cmd->argv_lens[1] = 128;
827 /* Send the NICK command */
828 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
829 1, 1, cmd->argv[1], cmd->argv_lens[1]);
831 /* Notify application */
832 COMMAND(SILC_STATUS_OK);
834 /** Wait for command reply */
835 silc_fsm_next(fsm, silc_client_command_reply_wait);
836 return SILC_FSM_CONTINUE;
839 return SILC_FSM_FINISH;
842 /********************************** LIST ************************************/
844 /* Command LIST. Lists channels on the current server. */
846 SILC_FSM_STATE(silc_client_command_list)
848 SilcClientCommandContext cmd = fsm_context;
849 SilcClientConnection conn = cmd->conn;
850 SilcChannelEntry channel;
851 SilcBuffer idp = NULL;
853 if (cmd->argc == 2) {
854 /* Get the Channel ID of the channel */
855 channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
857 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
861 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
863 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
864 1, 1, silc_buffer_datalen(idp));
866 silc_buffer_free(idp);
868 /* Notify application */
869 COMMAND(SILC_STATUS_OK);
871 /** Wait for command reply */
872 silc_fsm_next(fsm, silc_client_command_reply_wait);
873 return SILC_FSM_CONTINUE;
876 /********************************** TOPIC ***********************************/
878 /* Command TOPIC. Sets/shows topic on a channel. */
880 SILC_FSM_STATE(silc_client_command_topic)
882 SilcClientCommandContext cmd = fsm_context;
883 SilcClientConnection conn = cmd->conn;
884 SilcChannelEntry channel;
888 if (cmd->argc < 2 || cmd->argc > 3) {
889 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
890 "Usage: /TOPIC <channel> [<topic>]");
891 COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
892 SILC_STATUS_ERR_TOO_MANY_PARAMS));
896 if (cmd->argv[1][0] == '*') {
897 if (!conn->current_channel) {
898 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
901 name = conn->current_channel->channel_name;
906 if (!conn->current_channel) {
907 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
911 /* Get the Channel ID of the channel */
912 channel = silc_client_get_channel(conn->client, conn, name);
914 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
918 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
920 /* Send TOPIC command to the server */
922 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
923 1, silc_buffer_datalen(idp),
924 2, cmd->argv[2], strlen(cmd->argv[2]));
926 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
927 1, silc_buffer_datalen(idp));
929 silc_buffer_free(idp);
931 /* Notify application */
932 COMMAND(SILC_STATUS_OK);
934 /** Wait for command reply */
935 silc_fsm_next(fsm, silc_client_command_reply_wait);
936 return SILC_FSM_CONTINUE;
939 return SILC_FSM_FINISH;
942 /********************************* INVITE ***********************************/
944 /* Command INVITE. Invites specific client to join a channel. This is
945 also used to mange the invite list of the channel. */
947 SILC_FSM_STATE(silc_client_command_invite)
949 SilcClientCommandContext cmd = fsm_context;
950 SilcClientConnection conn = cmd->conn;
951 SilcClient client = conn->client;
952 SilcClientEntry client_entry = NULL;
953 SilcChannelEntry channel;
954 SilcBuffer clidp, chidp, args = NULL;
955 SilcPublicKey pubkey = NULL;
956 SilcDList clients = NULL;
957 char *nickname = NULL, *name;
959 unsigned char action[1];
962 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
963 "Usage: /INVITE <channel> [<nickname>[@server>]"
964 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
965 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
969 if (cmd->argv[1][0] == '*') {
970 if (!conn->current_channel) {
971 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
975 channel = conn->current_channel;
979 channel = silc_client_get_channel(conn->client, conn, name);
981 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
986 /* Parse the typed nickname. */
987 if (cmd->argc == 3) {
988 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
989 if (client->internal->params->nickname_parse)
990 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
992 nickname = strdup(cmd->argv[2]);
994 /* Find client entry */
995 clients = silc_client_get_clients_local(client, conn, nickname,
998 /* Resolve client information */
999 SILC_FSM_CALL(silc_client_get_clients(
1000 client, conn, nickname,
1002 silc_client_command_resolve_continue,
1005 client_entry = silc_dlist_get(clients);
1007 if (cmd->argv[2][0] == '+')
1012 /* Check if it is public key file to be added to invite list */
1013 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
1014 invite = cmd->argv[2];
1021 args = silc_buffer_alloc_size(2);
1022 silc_buffer_format(args,
1023 SILC_STR_UI_SHORT(1),
1026 chidp = silc_public_key_payload_encode(pubkey);
1027 args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1028 silc_buffer_len(chidp), 2);
1029 silc_buffer_free(chidp);
1030 silc_pkcs_public_key_free(pubkey);
1032 args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1036 /* Send the command */
1037 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1039 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1040 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1041 1, silc_buffer_datalen(chidp),
1042 2, silc_buffer_datalen(clidp),
1043 3, args ? action : NULL, args ? 1 : 0,
1044 4, silc_buffer_datalen(args));
1045 silc_buffer_free(clidp);
1047 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1048 1, silc_buffer_datalen(chidp),
1049 3, args ? action : NULL, args ? 1 : 0,
1050 4, silc_buffer_datalen(args));
1053 silc_buffer_free(chidp);
1054 silc_buffer_free(args);
1055 silc_free(nickname);
1056 silc_client_list_free(client, conn, clients);
1058 /* Notify application */
1059 COMMAND(SILC_STATUS_OK);
1061 /** Wait for command reply */
1062 silc_fsm_next(fsm, silc_client_command_reply_wait);
1063 return SILC_FSM_CONTINUE;
1066 silc_free(nickname);
1067 return SILC_FSM_FINISH;
1070 /********************************** QUIT ************************************/
1072 /* Close the connection */
1074 SILC_FSM_STATE(silc_client_command_quit_final)
1076 SilcClientCommandContext cmd = fsm_context;
1077 SilcClientConnection conn = cmd->conn;
1078 SilcClient client = conn->client;
1080 /* Notify application */
1081 COMMAND(SILC_STATUS_OK);
1083 /* Call connection callback */
1084 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
1085 0, NULL, conn->callback_context);
1087 /* Signal to close connection */
1088 if (!conn->internal->disconnected) {
1089 conn->internal->disconnected = TRUE;
1090 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
1093 return SILC_FSM_FINISH;
1096 /* Command QUIT. Closes connection with current server. */
1098 SILC_FSM_STATE(silc_client_command_quit)
1100 SilcClientCommandContext cmd = fsm_context;
1101 SilcClientConnection conn = cmd->conn;
1104 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1105 1, cmd->argv[1], cmd->argv_lens[1]);
1107 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1109 /* Sleep for a while */
1112 /* We close the connection with a little timeout */
1113 silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1114 return SILC_FSM_WAIT;
1117 /********************************** KILL ************************************/
1120 /* Command KILL. Router operator can use this command to remove an client
1121 fromthe SILC Network. */
1123 SILC_FSM_STATE(silc_client_command_kill)
1125 SilcClientCommandContext cmd = fsm_context;
1126 SilcClientConnection conn = cmd->conn;
1127 SilcClient client = conn->client;
1128 SilcBuffer idp, auth = NULL;
1129 SilcClientEntry target;
1131 char *nickname = NULL, *comment = NULL;
1133 if (cmd->argc < 2) {
1134 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1135 "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1136 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1137 return SILC_FSM_FINISH;
1140 /* Parse the typed nickname. */
1141 if (client->internal->params->nickname_parse)
1142 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
1144 nickname = strdup(cmd->argv[1]);
1146 return SILC_FSM_FINISH;
1148 /* Get the target client */
1149 clients = silc_client_get_clients_local(client, conn, nickname,
1152 /* Resolve client information */
1153 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
1155 silc_client_command_resolve_continue,
1158 target = silc_dlist_get(clients);
1160 if (cmd->argc >= 3) {
1161 if (strcasecmp(cmd->argv[2], "-pubkey"))
1162 comment = cmd->argv[2];
1164 if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1165 (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1166 /* Encode the public key authentication payload */
1167 auth = silc_auth_public_key_auth_generate(conn->public_key,
1170 conn->internal->sha1hash,
1171 &target->id, SILC_ID_CLIENT);
1175 /* Send the KILL command to the server */
1176 idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1177 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1178 1, silc_buffer_datalen(idp),
1179 2, comment, comment ? strlen(comment) : 0,
1180 3, silc_buffer_datalen(auth));
1181 silc_buffer_free(idp);
1182 silc_buffer_free(auth);
1183 silc_free(nickname);
1184 silc_client_list_free(client, conn, clients);
1186 /* Notify application */
1187 COMMAND(SILC_STATUS_OK);
1189 /** Wait for command reply */
1190 silc_fsm_next(fsm, silc_client_command_reply_wait);
1191 return SILC_FSM_CONTINUE;
1194 /********************************** INFO ************************************/
1196 /* Command INFO. Request information about specific server. If specific
1197 server is not provided the current server is used. */
1199 SILC_FSM_STATE(silc_client_command_info)
1201 SilcClientCommandContext cmd = fsm_context;
1202 SilcClientConnection conn = cmd->conn;
1204 /* Send the command */
1206 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1207 1, cmd->argv[1], cmd->argv_lens[1]);
1209 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1211 /* Notify application */
1212 COMMAND(SILC_STATUS_OK);
1214 /** Wait for command reply */
1215 silc_fsm_next(fsm, silc_client_command_reply_wait);
1216 return SILC_FSM_CONTINUE;
1219 /********************************** STATS ***********************************/
1221 /* Command STATS. Shows server and network statistics. */
1223 SILC_FSM_STATE(silc_client_command_stats)
1225 SilcClientCommandContext cmd = fsm_context;
1226 SilcClientConnection conn = cmd->conn;
1228 /* Send the command */
1229 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1230 1, silc_buffer_datalen(conn->internal->
1233 /* Notify application */
1234 COMMAND(SILC_STATUS_OK);
1236 /** Wait for command reply */
1237 silc_fsm_next(fsm, silc_client_command_reply_wait);
1238 return SILC_FSM_CONTINUE;
1241 /********************************** PING ************************************/
1243 /* Command PING. Sends ping to server. */
1245 SILC_FSM_STATE(silc_client_command_ping)
1247 SilcClientCommandContext cmd = fsm_context;
1248 SilcClientConnection conn = cmd->conn;
1250 if (cmd->argc < 2) {
1251 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1252 return SILC_FSM_FINISH;
1255 /* Send the command */
1256 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1257 1, silc_buffer_datalen(conn->internal->
1260 /* Save ping time */
1261 cmd->context = SILC_64_TO_PTR(silc_time());
1263 /* Notify application */
1264 COMMAND(SILC_STATUS_OK);
1266 /** Wait for command reply */
1267 silc_fsm_next(fsm, silc_client_command_reply_wait);
1268 return SILC_FSM_CONTINUE;
1271 /********************************** JOIN ************************************/
1273 /* Command JOIN. Joins to a channel. */
1275 SILC_FSM_STATE(silc_client_command_join)
1277 SilcClientCommandContext cmd = fsm_context;
1278 SilcClientConnection conn = cmd->conn;
1279 SilcChannelEntry channel;
1280 SilcBuffer auth = NULL, cauth = NULL;
1281 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1282 int i, passphrase_len = 0;
1284 if (cmd->argc < 2) {
1285 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1289 /* See if we have joined to the requested channel already */
1290 channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1291 if (channel && silc_client_on_channel(channel, conn->local_entry))
1294 if (cmd->argv_lens[1] > 256)
1295 cmd->argv_lens[1] = 256;
1297 name = cmd->argv[1];
1299 for (i = 2; i < cmd->argc; i++) {
1300 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1301 cipher = cmd->argv[++i];
1302 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1303 hmac = cmd->argv[++i];
1304 } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1305 auth = silc_auth_public_key_auth_generate(conn->public_key,
1308 conn->internal->sha1hash,
1311 } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1312 SilcPublicKey pubkey = conn->public_key;
1313 SilcPrivateKey privkey = conn->private_key;
1314 unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1317 if (cmd->argc >= i + 3) {
1319 if (cmd->argc >= i + 4) {
1320 pass = cmd->argv[i + 3];
1323 if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1324 &pubkey, &privkey)) {
1325 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1326 "Could not load key pair, check your arguments");
1327 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1333 pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1334 silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1336 pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1337 memcpy(pubdata, pkhash, 20);
1338 cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1340 conn->internal->sha1hash,
1343 memset(pubdata, 0, 128);
1346 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1347 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1348 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1349 cmd->argv_lens[i], 0);
1350 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1351 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1352 0, pu8, passphrase_len);
1355 passphrase = strdup(cmd->argv[i]);
1356 passphrase_len = cmd->argv_lens[i];
1361 /* Send JOIN command to the server */
1362 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1363 1, name, strlen(name),
1364 2, silc_buffer_datalen(conn->internal->
1366 3, passphrase, passphrase_len,
1367 4, cipher, cipher ? strlen(cipher) : 0,
1368 5, hmac, hmac ? strlen(hmac) : 0,
1369 6, silc_buffer_datalen(auth),
1370 7, silc_buffer_datalen(cauth));
1372 silc_buffer_free(auth);
1373 silc_buffer_free(cauth);
1375 memset(passphrase, 0, strlen(passphrase));
1376 silc_free(passphrase);
1378 /* Notify application */
1379 COMMAND(SILC_STATUS_OK);
1381 /** Wait for command reply */
1382 silc_fsm_next(fsm, silc_client_command_reply_wait);
1383 return SILC_FSM_CONTINUE;
1386 return SILC_FSM_FINISH;
1389 /********************************** MOTD ************************************/
1391 /* MOTD command. Requests motd from server. */
1393 SILC_FSM_STATE(silc_client_command_motd)
1395 SilcClientCommandContext cmd = fsm_context;
1396 SilcClientConnection conn = cmd->conn;
1398 if (cmd->argc < 1 || cmd->argc > 2) {
1399 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1400 "Usage: /MOTD [<server>]");
1401 COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1402 SILC_STATUS_ERR_TOO_MANY_PARAMS));
1403 return SILC_FSM_FINISH;
1406 /* Send the command */
1408 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1409 1, conn->remote_host,
1410 strlen(conn->remote_host));
1412 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1413 1, cmd->argv[1], cmd->argv_lens[1]);
1415 /* Notify application */
1416 COMMAND(SILC_STATUS_OK);
1418 /** Wait for command reply */
1419 silc_fsm_next(fsm, silc_client_command_reply_wait);
1420 return SILC_FSM_CONTINUE;
1423 /********************************** UMODE ***********************************/
1425 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1426 modes as client cannot set itself server/router operator privileges. */
1428 SILC_FSM_STATE(silc_client_command_umode)
1430 SilcClientCommandContext cmd = fsm_context;
1431 SilcClientConnection conn = cmd->conn;
1432 unsigned char *cp, modebuf[4];
1433 SilcUInt32 mode, add, len;
1436 if (cmd->argc < 2) {
1437 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1438 "Usage: /UMODE +|-<modes>");
1439 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1440 return SILC_FSM_FINISH;
1443 mode = conn->local_entry->mode;
1445 /* Are we adding or removing mode */
1446 if (cmd->argv[1][0] == '-')
1452 cp = cmd->argv[1] + 1;
1454 for (i = 0; i < len; i++) {
1459 mode |= SILC_UMODE_SERVER_OPERATOR;
1460 mode |= SILC_UMODE_ROUTER_OPERATOR;
1461 mode |= SILC_UMODE_GONE;
1462 mode |= SILC_UMODE_INDISPOSED;
1463 mode |= SILC_UMODE_BUSY;
1464 mode |= SILC_UMODE_PAGE;
1465 mode |= SILC_UMODE_HYPER;
1466 mode |= SILC_UMODE_ROBOT;
1467 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1468 mode |= SILC_UMODE_REJECT_WATCHING;
1470 mode = SILC_UMODE_NONE;
1475 mode |= SILC_UMODE_SERVER_OPERATOR;
1477 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1481 mode |= SILC_UMODE_ROUTER_OPERATOR;
1483 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1487 mode |= SILC_UMODE_GONE;
1489 mode &= ~SILC_UMODE_GONE;
1493 mode |= SILC_UMODE_INDISPOSED;
1495 mode &= ~SILC_UMODE_INDISPOSED;
1499 mode |= SILC_UMODE_BUSY;
1501 mode &= ~SILC_UMODE_BUSY;
1505 mode |= SILC_UMODE_PAGE;
1507 mode &= ~SILC_UMODE_PAGE;
1511 mode |= SILC_UMODE_HYPER;
1513 mode &= ~SILC_UMODE_HYPER;
1517 mode |= SILC_UMODE_ROBOT;
1519 mode &= ~SILC_UMODE_ROBOT;
1523 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1525 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1529 mode |= SILC_UMODE_REJECT_WATCHING;
1531 mode &= ~SILC_UMODE_REJECT_WATCHING;
1535 mode |= SILC_UMODE_BLOCK_INVITE;
1537 mode &= ~SILC_UMODE_BLOCK_INVITE;
1540 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1541 return SILC_FSM_FINISH;
1546 SILC_PUT32_MSB(mode, modebuf);
1548 /* Send the command */
1549 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1550 1, silc_buffer_datalen(conn->internal->
1552 2, modebuf, sizeof(modebuf));
1554 /* Notify application */
1555 COMMAND(SILC_STATUS_OK);
1557 /** Wait for command reply */
1558 silc_fsm_next(fsm, silc_client_command_reply_wait);
1559 return SILC_FSM_CONTINUE;
1562 /********************************** CMODE ***********************************/
1564 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1565 can be set several at once. Those modes that require argument must be set
1566 separately (unless set with modes that does not require arguments). */
1568 SILC_FSM_STATE(silc_client_command_cmode)
1570 SilcClientCommandContext cmd = fsm_context;
1571 SilcClientConnection conn = cmd->conn;
1572 SilcClient client = conn->client;
1573 SilcChannelEntry channel;
1574 SilcBuffer chidp, auth = NULL, pk = NULL;
1575 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1576 SilcUInt32 mode, add, type, len, arg_len = 0;
1579 if (cmd->argc < 3) {
1580 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1581 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1582 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1586 if (cmd->argv[1][0] == '*') {
1587 if (!conn->current_channel) {
1588 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1592 channel = conn->current_channel;
1594 name = cmd->argv[1];
1596 channel = silc_client_get_channel(conn->client, conn, name);
1598 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1603 mode = channel->mode;
1605 /* Are we adding or removing mode */
1606 if (cmd->argv[2][0] == '-')
1611 /* Argument type to be sent to server */
1615 cp = cmd->argv[2] + 1;
1617 for (i = 0; i < len; i++) {
1621 mode |= SILC_CHANNEL_MODE_PRIVATE;
1623 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1627 mode |= SILC_CHANNEL_MODE_SECRET;
1629 mode &= ~SILC_CHANNEL_MODE_SECRET;
1633 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1635 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1639 mode |= SILC_CHANNEL_MODE_INVITE;
1641 mode &= ~SILC_CHANNEL_MODE_INVITE;
1645 mode |= SILC_CHANNEL_MODE_TOPIC;
1647 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1651 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1653 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1657 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1659 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1664 mode |= SILC_CHANNEL_MODE_ULIMIT;
1666 if (cmd->argc < 4) {
1667 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1668 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1669 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1672 ll = atoi(cmd->argv[3]);
1673 SILC_PUT32_MSB(ll, tmp);
1677 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1682 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1684 if (cmd->argc < 4) {
1685 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1686 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1687 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1691 arg_len = cmd->argv_lens[3];
1693 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1698 mode |= SILC_CHANNEL_MODE_CIPHER;
1700 if (cmd->argc < 4) {
1701 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1702 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1703 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1707 arg_len = cmd->argv_lens[3];
1709 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1714 mode |= SILC_CHANNEL_MODE_HMAC;
1716 if (cmd->argc < 4) {
1717 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1718 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1719 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1723 arg_len = cmd->argv_lens[3];
1725 mode &= ~SILC_CHANNEL_MODE_HMAC;
1730 SilcPublicKey pubkey = conn->public_key;
1731 SilcPrivateKey privkey = conn->private_key;
1733 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1736 if (cmd->argc >= 5) {
1739 pass = cmd->argv[5];
1740 if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1741 &pubkey, &privkey)) {
1742 SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1743 "Could not load key pair, check your arguments");
1744 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1749 pk = silc_public_key_payload_encode(pubkey);
1750 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1752 conn->internal->sha1hash,
1755 arg = silc_buffer_data(auth);
1756 arg_len = silc_buffer_len(auth);
1758 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1764 SilcBool chadd = FALSE;
1765 SilcPublicKey chpk = NULL;
1767 mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1770 if (cmd->argc == 3) {
1771 /* Send empty command to receive the public key list. */
1772 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1773 silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1775 1, silc_buffer_datalen(chidp));
1776 silc_buffer_free(chidp);
1778 /* Notify application */
1779 COMMAND(SILC_STATUS_OK);
1783 if (cmd->argc >= 4) {
1784 auth = silc_buffer_alloc_size(2);
1785 silc_buffer_format(auth,
1786 SILC_STR_UI_SHORT(cmd->argc - 3),
1790 for (k = 3; k < cmd->argc; k++) {
1791 if (cmd->argv[k][0] == '+')
1793 if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1794 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1795 "Could not load public key %s, check the filename",
1797 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1798 silc_buffer_free(auth);
1803 pk = silc_public_key_payload_encode(chpk);
1804 auth = silc_argument_payload_encode_one(auth,
1805 silc_buffer_datalen(pk),
1806 chadd ? 0x00 : 0x01);
1807 silc_pkcs_public_key_free(chpk);
1808 silc_buffer_free(pk);
1813 arg = silc_buffer_data(auth);
1814 arg_len = silc_buffer_len(auth);
1816 mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1820 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1826 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1827 SILC_PUT32_MSB(mode, modebuf);
1829 /* Send the command. We support sending only one mode at once that
1830 requires an argument. */
1832 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1833 1, silc_buffer_datalen(chidp),
1834 2, modebuf, sizeof(modebuf),
1836 8, silc_buffer_datalen(pk));
1838 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1839 1, silc_buffer_datalen(chidp),
1840 2, modebuf, sizeof(modebuf));
1843 silc_buffer_free(chidp);
1844 silc_buffer_free(auth);
1845 silc_buffer_free(pk);
1847 /* Notify application */
1848 COMMAND(SILC_STATUS_OK);
1850 /** Wait for command reply */
1851 silc_fsm_next(fsm, silc_client_command_reply_wait);
1852 return SILC_FSM_CONTINUE;
1855 return SILC_FSM_FINISH;
1858 /********************************* CUMODE ***********************************/
1860 /* CUMODE command. Changes client's mode on a channel. */
1862 SILC_FSM_STATE(silc_client_command_cumode)
1864 SilcClientCommandContext cmd = fsm_context;
1865 SilcClientConnection conn = cmd->conn;
1866 SilcClient client = conn->client;
1867 SilcChannelEntry channel;
1868 SilcChannelUser chu;
1869 SilcClientEntry client_entry;
1870 SilcBuffer clidp, chidp, auth = NULL;
1871 SilcDList clients = NULL;
1872 unsigned char *name, *cp, modebuf[4];
1873 SilcUInt32 mode = 0, add, len;
1874 char *nickname = NULL;
1877 if (cmd->argc < 4) {
1878 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1879 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1880 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1884 if (cmd->argv[1][0] == '*') {
1885 if (!conn->current_channel) {
1886 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1890 channel = conn->current_channel;
1892 name = cmd->argv[1];
1894 channel = silc_client_get_channel(conn->client, conn, name);
1896 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1901 /* Parse the typed nickname. */
1902 if (client->internal->params->nickname_parse)
1903 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1905 nickname = strdup(cmd->argv[3]);
1907 /* Find client entry */
1908 clients = silc_client_get_clients_local(client, conn, nickname,
1911 /* Resolve client information */
1912 SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1913 silc_client_command_resolve_continue,
1916 client_entry = silc_dlist_get(clients);
1918 /* Get the current mode */
1919 chu = silc_client_on_channel(channel, client_entry);
1923 /* Are we adding or removing mode */
1924 if (cmd->argv[2][0] == '-')
1930 cp = cmd->argv[2] + 1;
1932 for (i = 0; i < len; i++) {
1936 mode |= SILC_CHANNEL_UMODE_CHANFO;
1937 mode |= SILC_CHANNEL_UMODE_CHANOP;
1938 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1939 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1940 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1942 mode = SILC_CHANNEL_UMODE_NONE;
1947 SilcPublicKey pubkey = conn->public_key;
1948 SilcPrivateKey privkey = conn->private_key;
1950 if (cmd->argc >= 6) {
1953 pass = cmd->argv[6];
1954 if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1955 &pubkey, &privkey)) {
1956 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1957 "Could not load key pair, check your arguments");
1958 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1963 auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1965 conn->internal->sha1hash,
1968 mode |= SILC_CHANNEL_UMODE_CHANFO;
1970 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1975 mode |= SILC_CHANNEL_UMODE_CHANOP;
1977 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1981 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1983 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1987 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1989 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1993 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1995 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1999 mode |= SILC_CHANNEL_UMODE_QUIET;
2001 mode &= ~SILC_CHANNEL_UMODE_QUIET;
2004 COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2010 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2011 SILC_PUT32_MSB(mode, modebuf);
2012 clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2014 /* Send the command packet. We support sending only one mode at once
2015 that requires an argument. */
2016 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2017 1, silc_buffer_datalen(chidp),
2019 3, silc_buffer_datalen(clidp),
2020 4, silc_buffer_datalen(auth));
2022 silc_buffer_free(chidp);
2023 silc_buffer_free(clidp);
2025 silc_buffer_free(auth);
2026 silc_free(nickname);
2027 silc_client_list_free(client, conn, clients);
2029 /* Notify application */
2030 COMMAND(SILC_STATUS_OK);
2032 /** Wait for command reply */
2033 silc_fsm_next(fsm, silc_client_command_reply_wait);
2034 return SILC_FSM_CONTINUE;
2037 silc_client_list_free(client, conn, clients);
2038 silc_free(nickname);
2039 return SILC_FSM_FINISH;
2042 /********************************** KICK ************************************/
2044 /* KICK command. Kicks a client out of channel. */
2046 SILC_FSM_STATE(silc_client_command_kick)
2048 SilcClientCommandContext cmd = fsm_context;
2049 SilcClientConnection conn = cmd->conn;
2050 SilcClient client = conn->client;
2051 SilcChannelEntry channel;
2052 SilcBuffer idp, idp2;
2053 SilcClientEntry target;
2054 SilcDList clients = NULL;
2056 char *nickname = NULL;
2058 if (cmd->argc < 3) {
2059 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2060 "Usage: /KICK <channel> <nickname> [<comment>]");
2061 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2065 if (cmd->argv[1][0] == '*') {
2066 if (!conn->current_channel) {
2067 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2070 name = conn->current_channel->channel_name;
2072 name = cmd->argv[1];
2075 if (!conn->current_channel) {
2076 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2080 /* Get the Channel ID of the channel */
2081 channel = silc_client_get_channel(conn->client, conn, name);
2083 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2087 /* Parse the typed nickname. */
2088 if (client->internal->params->nickname_parse)
2089 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
2091 nickname = strdup(cmd->argv[2]);
2093 /* Get the target client */
2094 clients = silc_client_get_clients_local(client, conn, nickname,
2097 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2098 "No such client: %s", cmd->argv[2]);
2099 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2102 target = silc_dlist_get(clients);
2104 /* Send KICK command to the server */
2105 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2106 idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2108 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2109 1, silc_buffer_datalen(idp),
2110 2, silc_buffer_datalen(idp2));
2112 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2113 1, silc_buffer_datalen(idp),
2114 2, silc_buffer_datalen(idp2),
2115 3, cmd->argv[3], strlen(cmd->argv[3]));
2117 silc_buffer_free(idp);
2118 silc_buffer_free(idp2);
2119 silc_free(nickname);
2120 silc_client_list_free(client, conn, clients);
2122 /* Notify application */
2123 COMMAND(SILC_STATUS_OK);
2125 /** Wait for command reply */
2126 silc_fsm_next(fsm, silc_client_command_reply_wait);
2127 return SILC_FSM_CONTINUE;
2130 silc_free(nickname);
2131 return SILC_FSM_FINISH;
2134 /***************************** OPER & SILCOPER ******************************/
2137 unsigned char *passphrase;
2138 SilcUInt32 passphrase_len;
2139 } *SilcClientCommandOper;
2141 /* Ask passphrase callback */
2143 static void silc_client_command_oper_cb(unsigned char *data,
2144 SilcUInt32 data_len, void *context)
2146 SilcClientCommandContext cmd = context;
2147 SilcClientCommandOper oper = cmd->context;
2149 if (data && data_len)
2150 oper->passphrase = silc_memdup(data, data_len);
2151 oper->passphrase_len = data_len;
2154 SILC_FSM_CALL_CONTINUE(&cmd->thread);
2157 /* Send OPER/SILCOPER command */
2159 SILC_FSM_STATE(silc_client_command_oper_send)
2161 SilcClientCommandContext cmd = fsm_context;
2162 SilcClientConnection conn = cmd->conn;
2163 SilcClientCommandOper oper = cmd->context;
2166 if (!oper || !oper->passphrase) {
2167 /* Encode the public key authentication payload */
2168 auth = silc_auth_public_key_auth_generate(conn->public_key,
2171 conn->internal->hash,
2175 /* Encode the password authentication payload */
2176 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2177 oper->passphrase, oper->passphrase_len);
2180 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2181 1, cmd->argv[1], strlen(cmd->argv[1]),
2182 2, silc_buffer_datalen(auth));
2184 silc_buffer_clear(auth);
2185 silc_buffer_free(auth);
2187 silc_free(oper->passphrase);
2191 /* Notify application */
2192 COMMAND(SILC_STATUS_OK);
2194 /** Wait for command reply */
2195 silc_fsm_next(fsm, silc_client_command_reply_wait);
2196 return SILC_FSM_CONTINUE;
2199 /* OPER command. Used to obtain server operator privileges. */
2201 SILC_FSM_STATE(silc_client_command_oper)
2203 SilcClientCommandContext cmd = fsm_context;
2204 SilcClientConnection conn = cmd->conn;
2205 SilcClientCommandOper oper;
2207 if (cmd->argc < 2) {
2208 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2209 "Usage: /OPER <username> [-pubkey]");
2210 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2211 return SILC_FSM_FINISH;
2214 /* Get passphrase */
2215 if (cmd->argc < 3) {
2216 oper = silc_calloc(1, sizeof(*oper));
2218 return SILC_FSM_FINISH;
2219 cmd->context = oper;
2220 SILC_FSM_CALL(conn->client->internal->
2221 ops->ask_passphrase(conn->client, conn,
2222 silc_client_command_oper_cb, cmd));
2225 silc_fsm_next(fsm, silc_client_command_oper_send);
2226 return SILC_FSM_CONTINUE;
2229 /* SILCOPER command. Used to obtain router operator privileges. */
2231 SILC_FSM_STATE(silc_client_command_silcoper)
2233 SilcClientCommandContext cmd = fsm_context;
2234 SilcClientConnection conn = cmd->conn;
2235 SilcClientCommandOper oper;
2237 if (cmd->argc < 2) {
2238 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2239 "Usage: /SILCOPER <username> [-pubkey]");
2240 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2241 return SILC_FSM_FINISH;
2244 /* Get passphrase */
2245 if (cmd->argc < 3) {
2246 oper = silc_calloc(1, sizeof(*oper));
2248 return SILC_FSM_FINISH;
2249 cmd->context = oper;
2250 SILC_FSM_CALL(conn->client->internal->
2251 ops->ask_passphrase(conn->client, conn,
2252 silc_client_command_oper_cb, cmd));
2255 silc_fsm_next(fsm, silc_client_command_oper_send);
2256 return SILC_FSM_CONTINUE;
2259 /*********************************** BAN ************************************/
2261 /* Command BAN. This is used to manage the ban list of the channel. */
2263 SILC_FSM_STATE(silc_client_command_ban)
2265 SilcClientCommandContext cmd = fsm_context;
2266 SilcClientConnection conn = cmd->conn;
2267 SilcChannelEntry channel;
2268 SilcBuffer chidp, args = NULL;
2269 char *name, *ban = NULL;
2270 unsigned char action[1];
2271 SilcPublicKey pubkey = NULL;
2273 if (cmd->argc < 2) {
2274 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2275 "Usage: /BAN <channel> "
2276 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2277 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2281 if (cmd->argv[1][0] == '*') {
2282 if (!conn->current_channel) {
2283 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2287 channel = conn->current_channel;
2289 name = cmd->argv[1];
2291 channel = silc_client_get_channel(conn->client, conn, name);
2293 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2298 if (cmd->argc == 3) {
2299 if (cmd->argv[2][0] == '+')
2304 /* Check if it is public key file to be added to invite list */
2305 silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2312 args = silc_buffer_alloc_size(2);
2313 silc_buffer_format(args,
2314 SILC_STR_UI_SHORT(1),
2317 chidp = silc_public_key_payload_encode(pubkey);
2318 args = silc_argument_payload_encode_one(args,
2319 silc_buffer_datalen(chidp), 2);
2320 silc_buffer_free(chidp);
2321 silc_pkcs_public_key_free(pubkey);
2323 args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2327 chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2329 /* Send the command */
2330 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2331 1, silc_buffer_datalen(chidp),
2332 2, args ? action : NULL, args ? 1 : 0,
2333 3, silc_buffer_datalen(args));
2335 silc_buffer_free(chidp);
2336 silc_buffer_free(args);
2338 /* Notify application */
2339 COMMAND(SILC_STATUS_OK);
2341 /** Wait for command reply */
2342 silc_fsm_next(fsm, silc_client_command_reply_wait);
2343 return SILC_FSM_CONTINUE;
2346 return SILC_FSM_FINISH;
2349 /********************************* DETACH ***********************************/
2351 /* Command DETACH. This is used to detach from the server */
2353 SILC_FSM_STATE(silc_client_command_detach)
2355 SilcClientCommandContext cmd = fsm_context;
2356 SilcClientConnection conn = cmd->conn;
2358 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2360 /* Notify application */
2361 COMMAND(SILC_STATUS_OK);
2363 /** Wait for command reply */
2364 silc_fsm_next(fsm, silc_client_command_reply_wait);
2365 return SILC_FSM_CONTINUE;
2368 /********************************** WATCH ***********************************/
2370 /* Command WATCH. */
2372 SILC_FSM_STATE(silc_client_command_watch)
2374 SilcClientCommandContext cmd = fsm_context;
2375 SilcClientConnection conn = cmd->conn;
2376 SilcBuffer args = NULL;
2378 const char *pubkey = NULL;
2379 SilcBool pubkey_add = TRUE;
2381 if (cmd->argc < 3) {
2382 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2386 if (!strcasecmp(cmd->argv[1], "-add")) {
2388 } else if (!strcasecmp(cmd->argv[1], "-del")) {
2390 } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2392 pubkey = cmd->argv[2] + 1;
2393 if (cmd->argv[2][0] == '-')
2396 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2404 if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2405 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2406 "Could not load public key %s, check the filename", pubkey);
2407 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2411 args = silc_buffer_alloc_size(2);
2412 silc_buffer_format(args,
2413 SILC_STR_UI_SHORT(1),
2415 buffer = silc_public_key_payload_encode(pk);
2416 args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2417 pubkey_add ? 0x00 : 0x01);
2418 silc_buffer_free(buffer);
2419 silc_pkcs_public_key_free(pk);
2422 /* Send the commmand */
2423 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2424 1, silc_buffer_datalen(conn->internal->
2426 type, pubkey ? args->data : cmd->argv[2],
2427 pubkey ? silc_buffer_len(args) :
2430 silc_buffer_free(args);
2432 /* Notify application */
2433 COMMAND(SILC_STATUS_OK);
2435 /** Wait for command reply */
2436 silc_fsm_next(fsm, silc_client_command_reply_wait);
2437 return SILC_FSM_CONTINUE;
2440 return SILC_FSM_FINISH;
2443 /********************************** LEAVE ***********************************/
2445 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2447 SILC_FSM_STATE(silc_client_command_leave)
2449 SilcClientCommandContext cmd = fsm_context;
2450 SilcClientConnection conn = cmd->conn;
2451 SilcChannelEntry channel;
2455 if (cmd->argc != 2) {
2456 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2457 "Usage: /LEAVE <channel>");
2458 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2462 if (cmd->argv[1][0] == '*') {
2463 if (!conn->current_channel) {
2464 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2467 name = conn->current_channel->channel_name;
2469 name = cmd->argv[1];
2472 /* Get the channel entry */
2473 channel = silc_client_get_channel(conn->client, conn, name);
2475 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2479 idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2481 /* Send LEAVE command to the server */
2482 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2483 1, silc_buffer_datalen(idp));
2485 silc_buffer_free(idp);
2487 /* Notify application */
2488 COMMAND(SILC_STATUS_OK);
2490 if (conn->current_channel == channel)
2491 conn->current_channel = NULL;
2493 /** Wait for command reply */
2494 silc_fsm_next(fsm, silc_client_command_reply_wait);
2495 return SILC_FSM_CONTINUE;
2498 return SILC_FSM_FINISH;
2501 /********************************** USERS ***********************************/
2503 /* Command USERS. Requests the USERS of the clients joined on requested
2506 SILC_FSM_STATE(silc_client_command_users)
2508 SilcClientCommandContext cmd = fsm_context;
2509 SilcClientConnection conn = cmd->conn;
2512 if (cmd->argc != 2) {
2513 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2514 "Usage: /USERS <channel>");
2515 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2519 if (cmd->argv[1][0] == '*') {
2520 if (!conn->current_channel) {
2521 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2524 name = conn->current_channel->channel_name;
2526 name = cmd->argv[1];
2529 /* Send USERS command to the server */
2530 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2531 2, name, strlen(name));
2533 /* Notify application */
2534 COMMAND(SILC_STATUS_OK);
2536 /** Wait for command reply */
2537 silc_fsm_next(fsm, silc_client_command_reply_wait);
2538 return SILC_FSM_CONTINUE;
2541 return SILC_FSM_FINISH;
2544 /********************************* GETKEY ***********************************/
2546 /* Command GETKEY. Used to fetch remote client's public key. */
2548 SILC_FSM_STATE(silc_client_command_getkey)
2550 SilcClientCommandContext cmd = fsm_context;
2551 SilcClientConnection conn = cmd->conn;
2552 SilcClient client = conn->client;
2553 SilcClientEntry client_entry;
2554 SilcServerEntry server_entry;
2556 char *nickname = NULL;
2559 if (cmd->argc < 2) {
2560 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2561 "Usage: /GETKEY <nickname or server name>");
2562 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2563 return SILC_FSM_FINISH;
2566 /* Parse the typed nickname. */
2567 if (client->internal->params->nickname_parse)
2568 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2570 nickname = strdup(cmd->argv[1]);
2572 COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2573 return SILC_FSM_FINISH;
2576 /* Find client entry */
2577 clients = silc_client_get_clients_local(client, conn, nickname,
2580 /* Check whether user requested server */
2581 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2582 if (!server_entry) {
2583 if (cmd->resolved) {
2584 /* Resolving didn't find anything. We should never get here as
2585 errors are handled in the resolving callback. */
2586 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2587 COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2588 return SILC_FSM_FINISH;
2591 /* No client or server exist with this name, query for both. */
2592 cmd->resolved = TRUE;
2593 SILC_FSM_CALL(silc_client_command_send(client, conn,
2594 SILC_COMMAND_IDENTIFY,
2595 silc_client_command_continue,
2598 strlen(cmd->argv[1]),
2600 strlen(cmd->argv[1])));
2603 idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2604 silc_client_unref_server(client, conn, server_entry);
2606 client_entry = silc_dlist_get(clients);
2607 idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2608 silc_client_list_free(client, conn, clients);
2611 /* Send the commmand */
2612 silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2613 1, silc_buffer_datalen(idp));
2615 silc_buffer_free(idp);
2616 silc_free(nickname);
2618 /* Notify application */
2619 COMMAND(SILC_STATUS_OK);
2621 /** Wait for command reply */
2622 silc_fsm_next(fsm, silc_client_command_reply_wait);
2623 return SILC_FSM_CONTINUE;
2626 /********************************* SERVICE **********************************/
2628 /* Command SERVICE. Negotiates service agreement with server. */
2629 /* XXX incomplete */
2631 SILC_FSM_STATE(silc_client_command_service)
2633 SilcClientCommandContext cmd = fsm_context;
2635 SilcClientConnection conn = cmd->conn;
2639 if (cmd->argc < 2) {
2640 SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2641 "Usage: /SERVICE [<service name>] [-pubkey]");
2642 COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2643 return SILC_FSM_FINISH;
2646 name = cmd->argv[1];
2648 /* Send SERVICE command to the server */
2649 buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2650 ++conn->cmd_ident, 1,
2651 1, name, strlen(name));
2652 silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2653 NULL, 0, NULL, NULL, buffer->data,
2655 silc_buffer_free(buffer);
2658 /* Notify application */
2659 COMMAND(SILC_STATUS_OK);
2661 /** Wait for command reply */
2662 silc_fsm_next(fsm, silc_client_command_reply_wait);
2663 return SILC_FSM_CONTINUE;
2666 /* Register all default commands provided by the client library for the
2669 void silc_client_commands_register(SilcClient client)
2671 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2674 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2675 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2676 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2677 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2678 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2679 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2680 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2681 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2682 SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2683 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2684 SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2685 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2686 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2687 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2688 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2689 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2690 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2691 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2692 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2693 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2694 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2695 SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2696 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2697 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2698 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2699 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2700 SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2703 /* Unregister all commands. */
2705 void silc_client_commands_unregister(SilcClient client)
2707 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2708 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2709 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2710 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2711 SILC_CLIENT_CMDU(list, LIST, "LIST");
2712 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2713 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2714 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2715 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2716 SILC_CLIENT_CMDU(info, INFO, "INFO");
2717 SILC_CLIENT_CMDU(stats, STATS, "STATS");
2718 SILC_CLIENT_CMDU(ping, PING, "PING");
2719 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2720 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2721 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2722 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2723 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2724 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2725 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2726 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2727 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2728 SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2729 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2730 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2731 SILC_CLIENT_CMDU(users, USERS, "USERS");
2732 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2733 SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2736 /****************** Client Side Incoming Command Handling *******************/
2738 /* Reply to WHOIS command from server */
2740 static void silc_client_command_process_whois(SilcClient client,
2741 SilcClientConnection conn,
2742 SilcCommandPayload payload,
2743 SilcArgumentPayload args)
2749 SilcBuffer buffer, packet;
2751 SILC_LOG_DEBUG(("Received WHOIS command"));
2753 /* Try to take the Requested Attributes */
2754 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2758 attrs = silc_attribute_payload_parse(tmp, tmp_len);
2762 /* Process requested attributes */
2763 buffer = silc_client_attributes_process(client, conn, attrs);
2765 silc_attribute_payload_list_free(attrs);
2769 /* Send the attributes back */
2771 silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2773 silc_command_get_ident(payload),
2774 1, 11, buffer->data, buffer->len);
2775 silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2776 NULL, 0, NULL, NULL, packet->data,
2778 silc_buffer_free(packet);
2779 silc_buffer_free(buffer);
2783 /* Client is able to receive some command packets even though they are
2784 special case. Server may send WHOIS command to the client to retrieve
2785 Requested Attributes information for WHOIS query the server is
2786 processing. This function currently handles only the WHOIS command,
2787 but if in the future more commands may arrive then this can be made
2788 to support other commands too. */
2790 SILC_FSM_STATE(silc_client_command)
2792 SilcClientConnection conn = fsm_context;
2793 SilcClient client = conn->client;
2794 SilcPacket packet = state_context;
2795 SilcCommandPayload payload;
2796 SilcCommand command;
2797 SilcArgumentPayload args;
2799 /* Get command payload from packet */
2800 payload = silc_command_payload_parse(packet->buffer.data,
2801 silc_buffer_len(&packet->buffer));
2803 SILC_LOG_DEBUG(("Bad command packet"));
2804 return SILC_FSM_FINISH;
2808 args = silc_command_get_args(payload);
2810 /* Get the command */
2811 command = silc_command_get(payload);
2814 case SILC_COMMAND_WHOIS:
2815 /* Ignore everything if requested by application */
2816 if (client->internal->params->ignore_requested_attributes)
2819 silc_client_command_process_whois(client, conn, payload, args);
2826 silc_command_payload_free(payload);
2827 return SILC_FSM_FINISH;