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),
36 { NULL, 0, NULL, 0, 0 },
39 /* Finds and returns a pointer to the command list. Return NULL if the
40 command is not found. */
42 SilcClientCommand *silc_client_local_command_find(const char *name)
44 SilcClientCommand *cmd;
46 for (cmd = silc_local_command_list; cmd->name; cmd++) {
47 if (!strcmp(cmd->name, name))
54 /* HELP command. This is local command and shows help on SILC */
56 SILC_CLIENT_LCMD_FUNC(help)
61 /* CLEAR command. This is local command and clears current output window */
63 SILC_CLIENT_LCMD_FUNC(clear)
65 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
67 silc_client_command_free(cmd);
70 /* VERSION command. This is local command and shows version of the client */
72 SILC_CLIENT_LCMD_FUNC(version)
74 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
75 SilcClient client = cmd->client;
76 extern char *silc_version;
77 extern char *silc_name;
78 extern char *silc_fullname;
80 silc_say(client, cmd->conn,
81 "%s (%s) version %s", silc_name, silc_fullname,
84 silc_client_command_free(cmd);
87 /* Command MSG. Sends private message to user or list of users. Note that
88 private messages are not really commands, they are message packets,
89 however, on user interface it is convenient to show them as commands
90 as that is the common way of sending private messages (like in IRC). */
91 /* XXX supports only one destination */
93 SILC_CLIENT_LCMD_FUNC(msg)
95 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
96 SilcClientConnection conn = cmd->conn;
97 SilcClient client = cmd->client;
98 SilcClientEntry client_entry = NULL;
100 char *nickname = NULL, *server = NULL;
103 silc_say(client, conn,
104 "You are not connected to a server, use /SERVER to connect");
109 silc_say(client, conn, "Usage: /MSG <nickname> <message>");
113 /* Parse the typed nickname. */
114 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
115 silc_say(client, conn, "Bad nickname");
119 /* Find client entry */
120 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
123 /* Client entry not found, it was requested thus mark this to be
125 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
126 NULL, silc_client_local_command_msg, context);
130 /* Display the message for our eyes. */
131 silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
133 /* Send the private message */
134 silc_client_send_private_message(client, conn, client_entry,
135 cmd->argv[2], cmd->argv_lens[2],
139 silc_client_command_free(cmd);
143 /* Command SERVER. Connects to remote SILC server. This is local command. */
145 SILC_CLIENT_LCMD_FUNC(server)
147 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
148 SilcClient client = cmd->client;
149 SilcClientConnection conn = cmd->conn;
150 int i = 0, len, port;
154 /* Show current servers */
157 silc_say(client, conn, "You are not connected to any server");
158 silc_say(client, conn, "Usage: /SERVER [<server>[:<port>]]");
162 silc_say(client, conn, "Current server: %s on %d %s",
163 conn->remote_host, conn->remote_port,
164 conn->remote_info ? conn->remote_info : "");
166 silc_say(client, conn, "Server list:");
167 for (i = 0; i < client->conns_count; i++) {
168 silc_say(client, conn, " [%d] %s on %d %s", i + 1,
169 client->conns[i]->remote_host,
170 client->conns[i]->remote_port,
171 client->conns[i]->remote_info ?
172 client->conns[i]->remote_info : "");
178 /* See if port is included and then extract it */
179 if (strchr(cmd->argv[1], ':')) {
180 len = strcspn(cmd->argv[1], ":");
181 hostname = silc_calloc(len + 1, sizeof(char));
182 memcpy(hostname, cmd->argv[1], len);
183 port = atoi(cmd->argv[1] + 1 + len);
185 hostname = cmd->argv[1];
190 if (conn && conn->remote_host) {
191 if (!strcmp(hostname, conn->remote_host) && port == conn->remote_port) {
192 silc_say(client, conn, "You are already connected to that server");
196 /* Close connection */
197 cmd->client->ops->disconnect(cmd->client, cmd->conn);
198 silc_client_close_connection(cmd->client, cmd->conn->sock);
202 /* Connect asynchronously to not to block user interface */
203 silc_client_connect_to_server(cmd->client, port, hostname, NULL);
206 silc_client_command_free(cmd);
209 /* Local command AWAY. Client replies with away message to whomever sends
210 private message to the client if the away message is set. If this is
211 given without arguments the away message is removed. */
213 SILC_CLIENT_LCMD_FUNC(away)
215 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
216 SilcClientConnection conn = cmd->conn;
217 SilcClient client = cmd->client;
218 SilcClientInternal app = (SilcClientInternal)client->application;
221 silc_say(client, conn,
222 "You are not connected to a server, use /SERVER to connect");
226 if (cmd->argc == 1) {
228 silc_free(conn->away->away);
229 silc_free(conn->away);
231 app->screen->bottom_line->away = FALSE;
233 silc_say(client, conn, "Away message removed");
234 silc_screen_print_bottom_line(app->screen, 0);
239 silc_free(conn->away->away);
241 conn->away = silc_calloc(1, sizeof(*conn->away));
243 app->screen->bottom_line->away = TRUE;
244 conn->away->away = strdup(cmd->argv[1]);
246 silc_say(client, conn, "Away message set: %s", conn->away->away);
247 silc_screen_print_bottom_line(app->screen, 0);
251 silc_client_command_free(cmd);
255 int type; /* 1 = msg, 2 = channel */
258 static SilcSKEKeyMaterial *curr_key = NULL;
260 /* Key agreement callback that is called after the key agreement protocol
261 has been performed. This is called also if error occured during the
262 key agreement protocol. The `key' is the allocated key material and
263 the caller is responsible of freeing it. The `key' is NULL if error
264 has occured. The application can freely use the `key' to whatever
265 purpose it needs. See lib/silcske/silcske.h for the definition of
266 the SilcSKEKeyMaterial structure. */
268 static void keyagr_completion(SilcClient client,
269 SilcClientConnection conn,
270 SilcClientEntry client_entry,
271 SilcKeyAgreementStatus status,
272 SilcSKEKeyMaterial *key,
275 KeyInternal i = (KeyInternal)context;
280 case SILC_KEY_AGREEMENT_OK:
281 silc_say(client, conn, "Key agreement compeleted successfully with %s",
282 client_entry->nickname);;
285 if (!silc_client_ask_yes_no(client,
286 "Would you like to use the key with private messages (y/n)? ")) {
287 silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
292 /* Set the private key for this client */
293 silc_client_del_private_message_key(client, conn, client_entry);
294 silc_client_add_private_message_key_ske(client, conn, client_entry,
296 silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
297 silc_ske_free_key_material(key);
302 case SILC_KEY_AGREEMENT_ERROR:
303 silc_say(client, conn, "Error occured during key agreement with %s",
304 client_entry->nickname);
307 case SILC_KEY_AGREEMENT_FAILURE:
308 silc_say(client, conn, "The key agreement failed with %s",
309 client_entry->nickname);
312 case SILC_KEY_AGREEMENT_TIMEOUT:
313 silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
314 client_entry->nickname);
325 /* Local command KEY. This command is used to set and unset private
326 keys for channels, set and unset private keys for private messages
327 with remote clients and to send key agreement requests and
328 negotiate the key agreement protocol with remote client. The
329 key agreement is supported only to negotiate private message keys,
330 it currently cannot be used to negotiate private keys for channels,
331 as it is not convenient for that purpose. */
333 SILC_CLIENT_LCMD_FUNC(key)
335 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
336 SilcClientConnection conn = cmd->conn;
337 SilcClient client = cmd->client;
338 SilcClientEntry client_entry = NULL;
339 SilcChannelEntry channel_entry = NULL;
340 unsigned int num = 0;
341 char *nickname = NULL, *server = NULL;
342 int command = 0, port = 0, type = 0;
343 char *hostname = NULL;
344 KeyInternal internal = NULL;
347 silc_say(client, conn,
348 "You are not connected to a server, use /SERVER to connect");
353 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
354 "set|unset|agreement|negotiate [<arguments>]");
359 if (!strcasecmp(cmd->argv[1], "msg"))
361 if (!strcasecmp(cmd->argv[1], "channel"))
365 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
366 "set|unset|agreement|negotiate [<arguments>]");
371 if (cmd->argv[2][0] == '*') {
374 /* Parse the typed nickname. */
375 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
376 silc_say(client, conn, "Bad nickname");
380 /* Find client entry */
381 client_entry = silc_idlist_get_client(client, conn, nickname,
384 /* Client entry not found, it was requested thus mark this to be
386 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
388 NULL, silc_client_local_command_key,
396 /* Get channel entry */
399 if (cmd->argv[2][0] == '*') {
400 if (!conn->current_channel) {
401 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
404 name = conn->current_channel->channel_name;
409 channel_entry = silc_client_get_channel(client, conn, name);
410 if (!channel_entry) {
411 silc_say(client, conn, "You are not on that channel");
417 if (!strcasecmp(cmd->argv[3], "set")) {
420 if (cmd->argc == 4) {
421 if (curr_key && type == 1 && client_entry) {
422 silc_client_del_private_message_key(client, conn, client_entry);
423 silc_client_add_private_message_key_ske(client, conn, client_entry,
429 if (cmd->argc >= 5) {
430 if (type == 1 && client_entry) {
431 /* Set private message key */
433 silc_client_del_private_message_key(client, conn, client_entry);
436 silc_client_add_private_message_key(client, conn, client_entry,
437 cmd->argv[5], cmd->argv[4],
439 (cmd->argv[4][0] == '*' ?
442 silc_client_add_private_message_key(client, conn, client_entry,
445 (cmd->argv[4][0] == '*' ?
448 /* Send the key to the remote client so that it starts using it
450 silc_client_send_private_message_key(client, conn, client_entry, TRUE);
451 } else if (type == 2) {
452 /* Set private channel key */
453 char *cipher = NULL, *hmac = NULL;
455 if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
456 silc_say(client, conn,
457 "Private key mode is not set on this channel");
462 cipher = cmd->argv[5];
466 if (!silc_client_add_channel_private_key(client, conn, channel_entry,
469 cmd->argv_lens[4])) {
470 silc_say(client, conn, "Could not add channel private key");
480 if (!strcasecmp(cmd->argv[3], "unset")) {
483 if (type == 1 && client_entry) {
484 /* Unset private message key */
485 silc_client_del_private_message_key(client, conn, client_entry);
486 } else if (type == 2) {
487 /* Unset channel key(s) */
488 SilcChannelPrivateKey *keys;
489 unsigned int keys_count;
493 silc_client_del_channel_private_keys(client, conn, channel_entry);
496 number = atoi(cmd->argv[4]);
497 keys = silc_client_list_channel_private_keys(client, conn,
503 if (!number || number > keys_count) {
504 silc_client_free_channel_private_keys(keys, keys_count);
508 silc_client_del_channel_private_key(client, conn, channel_entry,
510 silc_client_free_channel_private_keys(keys, keys_count);
518 if (!strcasecmp(cmd->argv[3], "list")) {
522 SilcPrivateMessageKeys keys;
523 unsigned int keys_count;
527 keys = silc_client_list_private_message_keys(client, conn,
532 /* list the private message key(s) */
533 if (nickname[0] == '*') {
534 silc_say(client, conn, "Private message keys");
535 silc_say(client, conn,
536 " Client Cipher Key");
537 for (k = 0; k < keys_count; k++) {
538 memset(buf, 0, sizeof(buf));
539 strncat(buf, " ", 2);
540 len = strlen(keys[k].client_entry->nickname);
541 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
543 for (i = 0; i < 30 - len; i++)
547 len = strlen(keys[k].cipher);
548 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
550 for (i = 0; i < 14 - len; i++)
555 strcat(buf, "<hidden>");
557 strcat(buf, "*generated*");
559 silc_say(client, conn, "%s", buf);
562 silc_say(client, conn, "Private message key",
563 client_entry->nickname);
564 silc_say(client, conn,
565 " Client Cipher Key");
566 for (k = 0; k < keys_count; k++) {
567 if (keys[k].client_entry != client_entry)
570 memset(buf, 0, sizeof(buf));
571 strncat(buf, " ", 2);
572 len = strlen(keys[k].client_entry->nickname);
573 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
575 for (i = 0; i < 30 - len; i++)
579 len = strlen(keys[k].cipher);
580 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
582 for (i = 0; i < 14 - len; i++)
587 strcat(buf, "<hidden>");
589 strcat(buf, "*generated*");
591 silc_say(client, conn, "%s", buf);
595 silc_client_free_private_message_keys(keys, keys_count);
596 } else if (type == 2) {
597 SilcChannelPrivateKey *keys;
598 unsigned int keys_count;
602 keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
607 silc_say(client, conn, "Channel %s private keys",
608 channel_entry->channel_name);
609 silc_say(client, conn,
611 for (k = 0; k < keys_count; k++) {
612 memset(buf, 0, sizeof(buf));
613 strncat(buf, " ", 2);
615 len = strlen(keys[k]->cipher->cipher->name);
616 strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
618 for (i = 0; i < 16 - len; i++)
622 len = strlen(keys[k]->hmac->hmac->name);
623 strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
625 for (i = 0; i < 16 - len; i++)
629 strcat(buf, "<hidden>");
631 silc_say(client, conn, "%s", buf);
634 silc_client_free_channel_private_keys(keys, keys_count);
640 /* Send command is used to send key agreement */
641 if (!strcasecmp(cmd->argv[3], "agreement")) {
645 hostname = cmd->argv[4];
647 port = atoi(cmd->argv[5]);
649 internal = silc_calloc(1, sizeof(*internal));
650 internal->type = type;
653 /* Start command is used to start key agreement (after receiving the
654 key_agreement client operation). */
655 if (!strcasecmp(cmd->argv[3], "negotiate")) {
659 hostname = cmd->argv[4];
661 port = atoi(cmd->argv[5]);
663 internal = silc_calloc(1, sizeof(*internal));
664 internal->type = type;
668 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
669 "set|unset|agreement|negotiate [<arguments>]");
673 if (command == 4 && client_entry) {
674 silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
675 silc_client_send_key_agreement(client, conn, client_entry, hostname,
676 port, 120, keyagr_completion, internal);
680 if (command == 5 && client_entry) {
681 silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
682 silc_client_perform_key_agreement(client, conn, client_entry, hostname,
683 port, keyagr_completion, internal);
692 silc_client_command_free(cmd);