5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "clientincludes.h"
23 #include "client_internal.h"
26 SilcClientCommand silc_local_command_list[] =
28 SILC_CLIENT_LCMD(help, HELP, "HELP", 0, 2),
29 SILC_CLIENT_LCMD(clear, CLEAR, "CLEAR", 0, 1),
30 SILC_CLIENT_LCMD(version, VERSION, "VERSION", 0, 1),
31 SILC_CLIENT_LCMD(server, SERVER, "SERVER", 0, 2),
32 SILC_CLIENT_LCMD(msg, MSG, "MSG", 0, 3),
33 SILC_CLIENT_LCMD(away, AWAY, "AWAY", 0, 2),
34 SILC_CLIENT_LCMD(key, KEY, "KEY", 0, 7),
35 SILC_CLIENT_LCMD(me, ME, "ME", 0, 3),
36 SILC_CLIENT_LCMD(notice, NOTICE, "NOTICE", 0, 3),
38 { NULL, 0, NULL, 0, 0 },
41 /* Finds and returns a pointer to the command list. Return NULL if the
42 command is not found. */
44 SilcClientCommand *silc_client_local_command_find(const char *name)
46 SilcClientCommand *cmd;
48 for (cmd = silc_local_command_list; cmd->name; cmd++) {
49 if (!strcmp(cmd->name, name))
56 /* HELP command. This is local command and shows help on SILC */
58 SILC_CLIENT_LCMD_FUNC(help)
63 /* CLEAR command. This is local command and clears current output window */
65 SILC_CLIENT_LCMD_FUNC(clear)
67 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
69 silc_client_command_free(cmd);
72 /* VERSION command. This is local command and shows version of the client */
74 SILC_CLIENT_LCMD_FUNC(version)
76 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
77 SilcClient client = cmd->client;
78 extern char *silc_version;
79 extern char *silc_name;
80 extern char *silc_fullname;
82 silc_say(client, cmd->conn,
83 "%s (%s) version %s", silc_name, silc_fullname,
86 silc_client_command_free(cmd);
89 /* Command MSG. Sends private message to user or list of users. Note that
90 private messages are not really commands, they are message packets,
91 however, on user interface it is convenient to show them as commands
92 as that is the common way of sending private messages (like in IRC). */
93 /* XXX supports only one destination */
95 SILC_CLIENT_LCMD_FUNC(msg)
97 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
98 SilcClientConnection conn = cmd->conn;
99 SilcClient client = cmd->client;
100 SilcClientEntry client_entry = NULL;
101 unsigned int num = 0;
102 char *nickname = NULL, *server = NULL;
105 silc_say(client, conn,
106 "You are not connected to a server, use /SERVER to connect");
111 silc_say(client, conn, "Usage: /MSG <nickname> <message>");
115 /* Parse the typed nickname. */
116 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
117 silc_say(client, conn, "Bad nickname");
121 /* Find client entry */
122 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
125 /* Client entry not found, it was requested thus mark this to be
127 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
128 NULL, silc_client_local_command_msg, context);
132 /* Display the message for our eyes. */
133 silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
135 /* Send the private message */
136 silc_client_send_private_message(client, conn, client_entry, 0,
137 cmd->argv[2], cmd->argv_lens[2],
141 silc_client_command_free(cmd);
145 /* Command SERVER. Connects to remote SILC server. This is local command. */
147 SILC_CLIENT_LCMD_FUNC(server)
149 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
150 SilcClient client = cmd->client;
151 SilcClientConnection conn = cmd->conn;
152 int i = 0, len, port;
156 /* Show current servers */
159 silc_say(client, conn, "You are not connected to any server");
160 silc_say(client, conn, "Usage: /SERVER [<server>[:<port>]]");
164 silc_say(client, conn, "Current server: %s on %d %s",
165 conn->remote_host, conn->remote_port,
166 conn->remote_info ? conn->remote_info : "");
168 silc_say(client, conn, "Server list:");
169 for (i = 0; i < client->conns_count; i++) {
170 silc_say(client, conn, " [%d] %s on %d %s", i + 1,
171 client->conns[i]->remote_host,
172 client->conns[i]->remote_port,
173 client->conns[i]->remote_info ?
174 client->conns[i]->remote_info : "");
180 /* See if port is included and then extract it */
181 if (strchr(cmd->argv[1], ':')) {
182 len = strcspn(cmd->argv[1], ":");
183 hostname = silc_calloc(len + 1, sizeof(char));
184 memcpy(hostname, cmd->argv[1], len);
185 port = atoi(cmd->argv[1] + 1 + len);
187 hostname = cmd->argv[1];
192 if (conn && conn->remote_host) {
193 if (!strcmp(hostname, conn->remote_host) && port == conn->remote_port) {
194 silc_say(client, conn, "You are already connected to that server");
198 /* Close connection */
199 cmd->client->ops->disconnect(cmd->client, cmd->conn);
200 silc_client_close_connection(cmd->client, cmd->conn->sock);
204 /* Connect asynchronously to not to block user interface */
205 silc_client_connect_to_server(cmd->client, port, hostname, NULL);
208 silc_client_command_free(cmd);
211 /* Local command AWAY. Client replies with away message to whomever sends
212 private message to the client if the away message is set. If this is
213 given without arguments the away message is removed. */
215 SILC_CLIENT_LCMD_FUNC(away)
217 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
218 SilcClientConnection conn = cmd->conn;
219 SilcClient client = cmd->client;
220 SilcClientInternal app = (SilcClientInternal)client->application;
223 silc_say(client, conn,
224 "You are not connected to a server, use /SERVER to connect");
228 if (cmd->argc == 1) {
230 silc_free(conn->away->away);
231 silc_free(conn->away);
233 app->screen->bottom_line->away = FALSE;
235 silc_say(client, conn, "Away message removed");
236 silc_screen_print_bottom_line(app->screen, 0);
241 silc_free(conn->away->away);
243 conn->away = silc_calloc(1, sizeof(*conn->away));
245 app->screen->bottom_line->away = TRUE;
246 conn->away->away = strdup(cmd->argv[1]);
248 silc_say(client, conn, "Away message set: %s", conn->away->away);
249 silc_screen_print_bottom_line(app->screen, 0);
253 silc_client_command_free(cmd);
257 int type; /* 1 = msg, 2 = channel */
260 static SilcSKEKeyMaterial *curr_key = NULL;
262 /* Key agreement callback that is called after the key agreement protocol
263 has been performed. This is called also if error occured during the
264 key agreement protocol. The `key' is the allocated key material and
265 the caller is responsible of freeing it. The `key' is NULL if error
266 has occured. The application can freely use the `key' to whatever
267 purpose it needs. See lib/silcske/silcske.h for the definition of
268 the SilcSKEKeyMaterial structure. */
270 static void keyagr_completion(SilcClient client,
271 SilcClientConnection conn,
272 SilcClientEntry client_entry,
273 SilcKeyAgreementStatus status,
274 SilcSKEKeyMaterial *key,
277 KeyInternal i = (KeyInternal)context;
282 case SILC_KEY_AGREEMENT_OK:
283 silc_say(client, conn, "Key agreement compeleted successfully with %s",
284 client_entry->nickname);;
287 if (!silc_client_ask_yes_no(client,
288 "Would you like to use the key with private messages (y/n)? ")) {
289 silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
294 /* Set the private key for this client */
295 silc_client_del_private_message_key(client, conn, client_entry);
296 silc_client_add_private_message_key_ske(client, conn, client_entry,
298 silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
299 silc_ske_free_key_material(key);
304 case SILC_KEY_AGREEMENT_ERROR:
305 silc_say(client, conn, "Error occured during key agreement with %s",
306 client_entry->nickname);
309 case SILC_KEY_AGREEMENT_FAILURE:
310 silc_say(client, conn, "The key agreement failed with %s",
311 client_entry->nickname);
314 case SILC_KEY_AGREEMENT_TIMEOUT:
315 silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
316 client_entry->nickname);
327 /* Local command KEY. This command is used to set and unset private
328 keys for channels, set and unset private keys for private messages
329 with remote clients and to send key agreement requests and
330 negotiate the key agreement protocol with remote client. The
331 key agreement is supported only to negotiate private message keys,
332 it currently cannot be used to negotiate private keys for channels,
333 as it is not convenient for that purpose. */
335 SILC_CLIENT_LCMD_FUNC(key)
337 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
338 SilcClientConnection conn = cmd->conn;
339 SilcClient client = cmd->client;
340 SilcClientEntry client_entry = NULL;
341 SilcChannelEntry channel_entry = NULL;
342 unsigned int num = 0;
343 char *nickname = NULL, *server = NULL;
344 int command = 0, port = 0, type = 0;
345 char *hostname = NULL;
346 KeyInternal internal = NULL;
349 silc_say(client, conn,
350 "You are not connected to a server, use /SERVER to connect");
355 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
356 "set|unset|agreement|negotiate [<arguments>]");
361 if (!strcasecmp(cmd->argv[1], "msg"))
363 if (!strcasecmp(cmd->argv[1], "channel"))
367 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
368 "set|unset|agreement|negotiate [<arguments>]");
373 if (cmd->argv[2][0] == '*') {
376 /* Parse the typed nickname. */
377 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
378 silc_say(client, conn, "Bad nickname");
382 /* Find client entry */
383 client_entry = silc_idlist_get_client(client, conn, nickname,
386 /* Client entry not found, it was requested thus mark this to be
388 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
390 NULL, silc_client_local_command_key,
398 /* Get channel entry */
401 if (cmd->argv[2][0] == '*') {
402 if (!conn->current_channel) {
403 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
406 name = conn->current_channel->channel_name;
411 channel_entry = silc_client_get_channel(client, conn, name);
412 if (!channel_entry) {
413 silc_say(client, conn, "You are not on that channel");
419 if (!strcasecmp(cmd->argv[3], "set")) {
422 if (cmd->argc == 4) {
423 if (curr_key && type == 1 && client_entry) {
424 silc_client_del_private_message_key(client, conn, client_entry);
425 silc_client_add_private_message_key_ske(client, conn, client_entry,
431 if (cmd->argc >= 5) {
432 if (type == 1 && client_entry) {
433 /* Set private message key */
435 silc_client_del_private_message_key(client, conn, client_entry);
438 silc_client_add_private_message_key(client, conn, client_entry,
439 cmd->argv[5], cmd->argv[4],
441 (cmd->argv[4][0] == '*' ?
444 silc_client_add_private_message_key(client, conn, client_entry,
447 (cmd->argv[4][0] == '*' ?
450 /* Send the key to the remote client so that it starts using it
452 silc_client_send_private_message_key(client, conn, client_entry, TRUE);
453 } else if (type == 2) {
454 /* Set private channel key */
455 char *cipher = NULL, *hmac = NULL;
457 if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
458 silc_say(client, conn,
459 "Private key mode is not set on this channel");
464 cipher = cmd->argv[5];
468 if (!silc_client_add_channel_private_key(client, conn, channel_entry,
471 cmd->argv_lens[4])) {
472 silc_say(client, conn, "Could not add channel private key");
482 if (!strcasecmp(cmd->argv[3], "unset")) {
485 if (type == 1 && client_entry) {
486 /* Unset private message key */
487 silc_client_del_private_message_key(client, conn, client_entry);
488 } else if (type == 2) {
489 /* Unset channel key(s) */
490 SilcChannelPrivateKey *keys;
491 unsigned int keys_count;
495 silc_client_del_channel_private_keys(client, conn, channel_entry);
498 number = atoi(cmd->argv[4]);
499 keys = silc_client_list_channel_private_keys(client, conn,
505 if (!number || number > keys_count) {
506 silc_client_free_channel_private_keys(keys, keys_count);
510 silc_client_del_channel_private_key(client, conn, channel_entry,
512 silc_client_free_channel_private_keys(keys, keys_count);
520 if (!strcasecmp(cmd->argv[3], "list")) {
524 SilcPrivateMessageKeys keys;
525 unsigned int keys_count;
529 keys = silc_client_list_private_message_keys(client, conn,
534 /* list the private message key(s) */
535 if (nickname[0] == '*') {
536 silc_say(client, conn, "Private message keys");
537 silc_say(client, conn,
538 " Client Cipher Key");
539 for (k = 0; k < keys_count; k++) {
540 memset(buf, 0, sizeof(buf));
541 strncat(buf, " ", 2);
542 len = strlen(keys[k].client_entry->nickname);
543 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
545 for (i = 0; i < 30 - len; i++)
549 len = strlen(keys[k].cipher);
550 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
552 for (i = 0; i < 14 - len; i++)
557 strcat(buf, "<hidden>");
559 strcat(buf, "*generated*");
561 silc_say(client, conn, "%s", buf);
564 silc_say(client, conn, "Private message key",
565 client_entry->nickname);
566 silc_say(client, conn,
567 " Client Cipher Key");
568 for (k = 0; k < keys_count; k++) {
569 if (keys[k].client_entry != client_entry)
572 memset(buf, 0, sizeof(buf));
573 strncat(buf, " ", 2);
574 len = strlen(keys[k].client_entry->nickname);
575 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
577 for (i = 0; i < 30 - len; i++)
581 len = strlen(keys[k].cipher);
582 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
584 for (i = 0; i < 14 - len; i++)
589 strcat(buf, "<hidden>");
591 strcat(buf, "*generated*");
593 silc_say(client, conn, "%s", buf);
597 silc_client_free_private_message_keys(keys, keys_count);
598 } else if (type == 2) {
599 SilcChannelPrivateKey *keys;
600 unsigned int keys_count;
604 keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
609 silc_say(client, conn, "Channel %s private keys",
610 channel_entry->channel_name);
611 silc_say(client, conn,
613 for (k = 0; k < keys_count; k++) {
614 memset(buf, 0, sizeof(buf));
615 strncat(buf, " ", 2);
617 len = strlen(keys[k]->cipher->cipher->name);
618 strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
620 for (i = 0; i < 16 - len; i++)
624 len = strlen(keys[k]->hmac->hmac->name);
625 strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
627 for (i = 0; i < 16 - len; i++)
631 strcat(buf, "<hidden>");
633 silc_say(client, conn, "%s", buf);
636 silc_client_free_channel_private_keys(keys, keys_count);
642 /* Send command is used to send key agreement */
643 if (!strcasecmp(cmd->argv[3], "agreement")) {
647 hostname = cmd->argv[4];
649 port = atoi(cmd->argv[5]);
651 internal = silc_calloc(1, sizeof(*internal));
652 internal->type = type;
655 /* Start command is used to start key agreement (after receiving the
656 key_agreement client operation). */
657 if (!strcasecmp(cmd->argv[3], "negotiate")) {
661 hostname = cmd->argv[4];
663 port = atoi(cmd->argv[5]);
665 internal = silc_calloc(1, sizeof(*internal));
666 internal->type = type;
670 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
671 "set|unset|agreement|negotiate [<arguments>]");
675 if (command == 4 && client_entry) {
676 silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
677 silc_client_send_key_agreement(client, conn, client_entry, hostname,
678 port, 120, keyagr_completion, internal);
682 if (command == 5 && client_entry) {
683 silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
684 silc_client_perform_key_agreement(client, conn, client_entry, hostname,
685 port, keyagr_completion, internal);
694 silc_client_command_free(cmd);
697 /* Sends an action to the channel. Equals CTCP's ACTION (IRC's /ME)
700 SILC_CLIENT_LCMD_FUNC(me)
702 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
703 SilcClientConnection conn = cmd->conn;
704 SilcClient client = cmd->client;
705 SilcChannelEntry channel_entry;
709 silc_say(client, conn,
710 "You are not connected to a server, use /SERVER to connect");
715 silc_say(client, conn, "Usage: /ME <channel> <action message>");
719 if (cmd->argv[1][0] == '*') {
720 if (!conn->current_channel) {
721 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
724 name = conn->current_channel->channel_name;
729 channel_entry = silc_client_get_channel(client, conn, name);
730 if (!channel_entry) {
731 silc_say(client, conn, "You are not on that channel");
735 /* Send the action message */
736 silc_client_send_channel_message(client, conn, channel_entry, NULL,
737 SILC_MESSAGE_FLAG_ACTION,
738 cmd->argv[2], cmd->argv_lens[2], TRUE);
740 silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
743 silc_client_command_free(cmd);
746 /* Sends an notice to the channel. */
748 SILC_CLIENT_LCMD_FUNC(notice)
750 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
751 SilcClientConnection conn = cmd->conn;
752 SilcClient client = cmd->client;
753 SilcChannelEntry channel_entry;
757 silc_say(client, conn,
758 "You are not connected to a server, use /SERVER to connect");
763 silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
767 if (cmd->argv[1][0] == '*') {
768 if (!conn->current_channel) {
769 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
772 name = conn->current_channel->channel_name;
777 channel_entry = silc_client_get_channel(client, conn, name);
778 if (!channel_entry) {
779 silc_say(client, conn, "You are not on that channel");
783 /* Send the action message */
784 silc_client_send_channel_message(client, conn, channel_entry, NULL,
785 SILC_MESSAGE_FLAG_NOTICE,
786 cmd->argv[2], cmd->argv_lens[2], TRUE);
788 silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
791 silc_client_command_free(cmd);