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),
37 { NULL, 0, NULL, 0, 0 },
40 /* Finds and returns a pointer to the command list. Return NULL if the
41 command is not found. */
43 SilcClientCommand *silc_client_local_command_find(const char *name)
45 SilcClientCommand *cmd;
47 for (cmd = silc_local_command_list; cmd->name; cmd++) {
48 if (!strcmp(cmd->name, name))
55 /* HELP command. This is local command and shows help on SILC */
57 SILC_CLIENT_LCMD_FUNC(help)
62 /* CLEAR command. This is local command and clears current output window */
64 SILC_CLIENT_LCMD_FUNC(clear)
66 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
68 silc_client_command_free(cmd);
71 /* VERSION command. This is local command and shows version of the client */
73 SILC_CLIENT_LCMD_FUNC(version)
75 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
76 SilcClient client = cmd->client;
77 extern char *silc_version;
78 extern char *silc_name;
79 extern char *silc_fullname;
81 silc_say(client, cmd->conn,
82 "%s (%s) version %s", silc_name, silc_fullname,
85 silc_client_command_free(cmd);
88 /* Command MSG. Sends private message to user or list of users. Note that
89 private messages are not really commands, they are message packets,
90 however, on user interface it is convenient to show them as commands
91 as that is the common way of sending private messages (like in IRC). */
92 /* XXX supports only one destination */
94 SILC_CLIENT_LCMD_FUNC(msg)
96 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
97 SilcClientConnection conn = cmd->conn;
98 SilcClient client = cmd->client;
99 SilcClientEntry client_entry = NULL;
100 unsigned int num = 0;
101 char *nickname = NULL, *server = NULL;
104 silc_say(client, conn,
105 "You are not connected to a server, use /SERVER to connect");
110 silc_say(client, conn, "Usage: /MSG <nickname> <message>");
114 /* Parse the typed nickname. */
115 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
116 silc_say(client, conn, "Bad nickname");
120 /* Find client entry */
121 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
124 /* Client entry not found, it was requested thus mark this to be
126 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
127 NULL, silc_client_local_command_msg, context);
131 /* Display the message for our eyes. */
132 silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
134 /* Send the private message */
135 silc_client_send_private_message(client, conn, client_entry, 0,
136 cmd->argv[2], cmd->argv_lens[2],
140 silc_client_command_free(cmd);
144 /* Command SERVER. Connects to remote SILC server. This is local command. */
146 SILC_CLIENT_LCMD_FUNC(server)
148 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
149 SilcClient client = cmd->client;
150 SilcClientConnection conn = cmd->conn;
151 int i = 0, len, port;
155 /* Show current servers */
158 silc_say(client, conn, "You are not connected to any server");
159 silc_say(client, conn, "Usage: /SERVER [<server>[:<port>]]");
163 silc_say(client, conn, "Current server: %s on %d %s",
164 conn->remote_host, conn->remote_port,
165 conn->remote_info ? conn->remote_info : "");
167 silc_say(client, conn, "Server list:");
168 for (i = 0; i < client->conns_count; i++) {
169 silc_say(client, conn, " [%d] %s on %d %s", i + 1,
170 client->conns[i]->remote_host,
171 client->conns[i]->remote_port,
172 client->conns[i]->remote_info ?
173 client->conns[i]->remote_info : "");
179 /* See if port is included and then extract it */
180 if (strchr(cmd->argv[1], ':')) {
181 len = strcspn(cmd->argv[1], ":");
182 hostname = silc_calloc(len + 1, sizeof(char));
183 memcpy(hostname, cmd->argv[1], len);
184 port = atoi(cmd->argv[1] + 1 + len);
186 hostname = cmd->argv[1];
191 if (conn && conn->remote_host) {
192 if (!strcmp(hostname, conn->remote_host) && port == conn->remote_port) {
193 silc_say(client, conn, "You are already connected to that server");
197 /* Close connection */
198 cmd->client->ops->disconnect(cmd->client, cmd->conn);
199 silc_client_close_connection(cmd->client, cmd->conn->sock);
203 /* Connect asynchronously to not to block user interface */
204 silc_client_connect_to_server(cmd->client, port, hostname, NULL);
207 silc_client_command_free(cmd);
210 /* Local command AWAY. Client replies with away message to whomever sends
211 private message to the client if the away message is set. If this is
212 given without arguments the away message is removed. */
214 SILC_CLIENT_LCMD_FUNC(away)
216 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
217 SilcClientConnection conn = cmd->conn;
218 SilcClient client = cmd->client;
219 SilcClientInternal app = (SilcClientInternal)client->application;
222 silc_say(client, conn,
223 "You are not connected to a server, use /SERVER to connect");
227 if (cmd->argc == 1) {
229 silc_free(conn->away->away);
230 silc_free(conn->away);
232 app->screen->bottom_line->away = FALSE;
234 silc_say(client, conn, "Away message removed");
235 silc_screen_print_bottom_line(app->screen, 0);
240 silc_free(conn->away->away);
242 conn->away = silc_calloc(1, sizeof(*conn->away));
244 app->screen->bottom_line->away = TRUE;
245 conn->away->away = strdup(cmd->argv[1]);
247 silc_say(client, conn, "Away message set: %s", conn->away->away);
248 silc_screen_print_bottom_line(app->screen, 0);
252 silc_client_command_free(cmd);
256 int type; /* 1 = msg, 2 = channel */
259 static SilcSKEKeyMaterial *curr_key = NULL;
261 /* Key agreement callback that is called after the key agreement protocol
262 has been performed. This is called also if error occured during the
263 key agreement protocol. The `key' is the allocated key material and
264 the caller is responsible of freeing it. The `key' is NULL if error
265 has occured. The application can freely use the `key' to whatever
266 purpose it needs. See lib/silcske/silcske.h for the definition of
267 the SilcSKEKeyMaterial structure. */
269 static void keyagr_completion(SilcClient client,
270 SilcClientConnection conn,
271 SilcClientEntry client_entry,
272 SilcKeyAgreementStatus status,
273 SilcSKEKeyMaterial *key,
276 KeyInternal i = (KeyInternal)context;
281 case SILC_KEY_AGREEMENT_OK:
282 silc_say(client, conn, "Key agreement compeleted successfully with %s",
283 client_entry->nickname);;
286 if (!silc_client_ask_yes_no(client,
287 "Would you like to use the key with private messages (y/n)? ")) {
288 silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
293 /* Set the private key for this client */
294 silc_client_del_private_message_key(client, conn, client_entry);
295 silc_client_add_private_message_key_ske(client, conn, client_entry,
297 silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
298 silc_ske_free_key_material(key);
303 case SILC_KEY_AGREEMENT_ERROR:
304 silc_say(client, conn, "Error occured during key agreement with %s",
305 client_entry->nickname);
308 case SILC_KEY_AGREEMENT_FAILURE:
309 silc_say(client, conn, "The key agreement failed with %s",
310 client_entry->nickname);
313 case SILC_KEY_AGREEMENT_TIMEOUT:
314 silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
315 client_entry->nickname);
326 /* Local command KEY. This command is used to set and unset private
327 keys for channels, set and unset private keys for private messages
328 with remote clients and to send key agreement requests and
329 negotiate the key agreement protocol with remote client. The
330 key agreement is supported only to negotiate private message keys,
331 it currently cannot be used to negotiate private keys for channels,
332 as it is not convenient for that purpose. */
334 SILC_CLIENT_LCMD_FUNC(key)
336 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
337 SilcClientConnection conn = cmd->conn;
338 SilcClient client = cmd->client;
339 SilcClientEntry client_entry = NULL;
340 SilcChannelEntry channel_entry = NULL;
341 unsigned int num = 0;
342 char *nickname = NULL, *server = NULL;
343 int command = 0, port = 0, type = 0;
344 char *hostname = NULL;
345 KeyInternal internal = NULL;
348 silc_say(client, conn,
349 "You are not connected to a server, use /SERVER to connect");
354 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
355 "set|unset|agreement|negotiate [<arguments>]");
360 if (!strcasecmp(cmd->argv[1], "msg"))
362 if (!strcasecmp(cmd->argv[1], "channel"))
366 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
367 "set|unset|agreement|negotiate [<arguments>]");
372 if (cmd->argv[2][0] == '*') {
375 /* Parse the typed nickname. */
376 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
377 silc_say(client, conn, "Bad nickname");
381 /* Find client entry */
382 client_entry = silc_idlist_get_client(client, conn, nickname,
385 /* Client entry not found, it was requested thus mark this to be
387 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
389 NULL, silc_client_local_command_key,
397 /* Get channel entry */
400 if (cmd->argv[2][0] == '*') {
401 if (!conn->current_channel) {
402 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
405 name = conn->current_channel->channel_name;
410 channel_entry = silc_client_get_channel(client, conn, name);
411 if (!channel_entry) {
412 silc_say(client, conn, "You are not on that channel");
418 if (!strcasecmp(cmd->argv[3], "set")) {
421 if (cmd->argc == 4) {
422 if (curr_key && type == 1 && client_entry) {
423 silc_client_del_private_message_key(client, conn, client_entry);
424 silc_client_add_private_message_key_ske(client, conn, client_entry,
430 if (cmd->argc >= 5) {
431 if (type == 1 && client_entry) {
432 /* Set private message key */
434 silc_client_del_private_message_key(client, conn, client_entry);
437 silc_client_add_private_message_key(client, conn, client_entry,
438 cmd->argv[5], cmd->argv[4],
440 (cmd->argv[4][0] == '*' ?
443 silc_client_add_private_message_key(client, conn, client_entry,
446 (cmd->argv[4][0] == '*' ?
449 /* Send the key to the remote client so that it starts using it
451 silc_client_send_private_message_key(client, conn, client_entry, TRUE);
452 } else if (type == 2) {
453 /* Set private channel key */
454 char *cipher = NULL, *hmac = NULL;
456 if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
457 silc_say(client, conn,
458 "Private key mode is not set on this channel");
463 cipher = cmd->argv[5];
467 if (!silc_client_add_channel_private_key(client, conn, channel_entry,
470 cmd->argv_lens[4])) {
471 silc_say(client, conn, "Could not add channel private key");
481 if (!strcasecmp(cmd->argv[3], "unset")) {
484 if (type == 1 && client_entry) {
485 /* Unset private message key */
486 silc_client_del_private_message_key(client, conn, client_entry);
487 } else if (type == 2) {
488 /* Unset channel key(s) */
489 SilcChannelPrivateKey *keys;
490 unsigned int keys_count;
494 silc_client_del_channel_private_keys(client, conn, channel_entry);
497 number = atoi(cmd->argv[4]);
498 keys = silc_client_list_channel_private_keys(client, conn,
504 if (!number || number > keys_count) {
505 silc_client_free_channel_private_keys(keys, keys_count);
509 silc_client_del_channel_private_key(client, conn, channel_entry,
511 silc_client_free_channel_private_keys(keys, keys_count);
519 if (!strcasecmp(cmd->argv[3], "list")) {
523 SilcPrivateMessageKeys keys;
524 unsigned int keys_count;
528 keys = silc_client_list_private_message_keys(client, conn,
533 /* list the private message key(s) */
534 if (nickname[0] == '*') {
535 silc_say(client, conn, "Private message keys");
536 silc_say(client, conn,
537 " Client Cipher Key");
538 for (k = 0; k < keys_count; k++) {
539 memset(buf, 0, sizeof(buf));
540 strncat(buf, " ", 2);
541 len = strlen(keys[k].client_entry->nickname);
542 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
544 for (i = 0; i < 30 - len; i++)
548 len = strlen(keys[k].cipher);
549 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
551 for (i = 0; i < 14 - len; i++)
556 strcat(buf, "<hidden>");
558 strcat(buf, "*generated*");
560 silc_say(client, conn, "%s", buf);
563 silc_say(client, conn, "Private message key",
564 client_entry->nickname);
565 silc_say(client, conn,
566 " Client Cipher Key");
567 for (k = 0; k < keys_count; k++) {
568 if (keys[k].client_entry != client_entry)
571 memset(buf, 0, sizeof(buf));
572 strncat(buf, " ", 2);
573 len = strlen(keys[k].client_entry->nickname);
574 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
576 for (i = 0; i < 30 - len; i++)
580 len = strlen(keys[k].cipher);
581 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
583 for (i = 0; i < 14 - len; i++)
588 strcat(buf, "<hidden>");
590 strcat(buf, "*generated*");
592 silc_say(client, conn, "%s", buf);
596 silc_client_free_private_message_keys(keys, keys_count);
597 } else if (type == 2) {
598 SilcChannelPrivateKey *keys;
599 unsigned int keys_count;
603 keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
608 silc_say(client, conn, "Channel %s private keys",
609 channel_entry->channel_name);
610 silc_say(client, conn,
612 for (k = 0; k < keys_count; k++) {
613 memset(buf, 0, sizeof(buf));
614 strncat(buf, " ", 2);
616 len = strlen(keys[k]->cipher->cipher->name);
617 strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
619 for (i = 0; i < 16 - len; i++)
623 len = strlen(keys[k]->hmac->hmac->name);
624 strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
626 for (i = 0; i < 16 - len; i++)
630 strcat(buf, "<hidden>");
632 silc_say(client, conn, "%s", buf);
635 silc_client_free_channel_private_keys(keys, keys_count);
641 /* Send command is used to send key agreement */
642 if (!strcasecmp(cmd->argv[3], "agreement")) {
646 hostname = cmd->argv[4];
648 port = atoi(cmd->argv[5]);
650 internal = silc_calloc(1, sizeof(*internal));
651 internal->type = type;
654 /* Start command is used to start key agreement (after receiving the
655 key_agreement client operation). */
656 if (!strcasecmp(cmd->argv[3], "negotiate")) {
660 hostname = cmd->argv[4];
662 port = atoi(cmd->argv[5]);
664 internal = silc_calloc(1, sizeof(*internal));
665 internal->type = type;
669 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
670 "set|unset|agreement|negotiate [<arguments>]");
674 if (command == 4 && client_entry) {
675 silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
676 silc_client_send_key_agreement(client, conn, client_entry, hostname,
677 port, 120, keyagr_completion, internal);
681 if (command == 5 && client_entry) {
682 silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
683 silc_client_perform_key_agreement(client, conn, client_entry, hostname,
684 port, keyagr_completion, internal);
693 silc_client_command_free(cmd);
696 /* Sends an action to the channel. Equals CTCP's ACTION (IRC's /ME)
699 SILC_CLIENT_LCMD_FUNC(me)
701 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
702 SilcClientConnection conn = cmd->conn;
703 SilcClient client = cmd->client;
704 SilcChannelEntry channel_entry;
708 silc_say(client, conn,
709 "You are not connected to a server, use /SERVER to connect");
714 silc_say(client, conn, "Usage: /ME <channel> <action message>");
718 if (cmd->argv[1][0] == '*') {
719 if (!conn->current_channel) {
720 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
723 name = conn->current_channel->channel_name;
728 channel_entry = silc_client_get_channel(client, conn, name);
729 if (!channel_entry) {
730 silc_say(client, conn, "You are not on that channel");
734 /* Send the action message */
735 silc_client_send_channel_message(client, conn, channel_entry, NULL,
736 SILC_MESSAGE_FLAG_ACTION,
737 cmd->argv[2], cmd->argv_lens[2], TRUE);
739 silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
742 silc_client_command_free(cmd);