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). */
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;
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;
220 unsigned char modebuf[4];
221 SilcBuffer idp, buffer;
224 silc_say(client, conn,
225 "You are not connected to a server, use /SERVER to connect");
229 if (cmd->argc == 1) {
230 conn->local_entry->mode &= ~SILC_UMODE_GONE;
233 silc_free(conn->away->away);
234 silc_free(conn->away);
236 app->screen->bottom_line->away = FALSE;
238 silc_say(client, conn, "Away message removed");
239 silc_screen_print_bottom_line(app->screen, 0);
242 conn->local_entry->mode |= SILC_UMODE_GONE;
245 silc_free(conn->away->away);
247 conn->away = silc_calloc(1, sizeof(*conn->away));
249 app->screen->bottom_line->away = TRUE;
250 conn->away->away = strdup(cmd->argv[1]);
252 silc_say(client, conn, "Away message set: %s", conn->away->away);
253 silc_screen_print_bottom_line(app->screen, 0);
256 /* Send the UMODE command to se myself as gone */
257 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
258 SILC_PUT32_MSB(conn->local_entry->mode, modebuf);
259 buffer = silc_command_payload_encode_va(SILC_COMMAND_UMODE,
260 ++conn->cmd_ident, 2,
261 1, idp->data, idp->len,
262 2, modebuf, sizeof(modebuf));
263 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
264 NULL, 0, NULL, NULL, buffer->data,
266 silc_buffer_free(buffer);
267 silc_buffer_free(idp);
270 silc_client_command_free(cmd);
274 int type; /* 1 = msg, 2 = channel */
277 static SilcSKEKeyMaterial *curr_key = NULL;
279 /* Key agreement callback that is called after the key agreement protocol
280 has been performed. This is called also if error occured during the
281 key agreement protocol. The `key' is the allocated key material and
282 the caller is responsible of freeing it. The `key' is NULL if error
283 has occured. The application can freely use the `key' to whatever
284 purpose it needs. See lib/silcske/silcske.h for the definition of
285 the SilcSKEKeyMaterial structure. */
287 static void keyagr_completion(SilcClient client,
288 SilcClientConnection conn,
289 SilcClientEntry client_entry,
290 SilcKeyAgreementStatus status,
291 SilcSKEKeyMaterial *key,
294 KeyInternal i = (KeyInternal)context;
299 case SILC_KEY_AGREEMENT_OK:
300 silc_say(client, conn, "Key agreement compeleted successfully with %s",
301 client_entry->nickname);;
304 /* Set the private key for this client */
305 silc_client_del_private_message_key(client, conn, client_entry);
306 silc_client_add_private_message_key_ske(client, conn, client_entry,
308 silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
309 silc_ske_free_key_material(key);
314 case SILC_KEY_AGREEMENT_ERROR:
315 silc_say(client, conn, "Error occured during key agreement with %s",
316 client_entry->nickname);
319 case SILC_KEY_AGREEMENT_FAILURE:
320 silc_say(client, conn, "The key agreement failed with %s",
321 client_entry->nickname);
324 case SILC_KEY_AGREEMENT_TIMEOUT:
325 silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
326 client_entry->nickname);
337 /* Local command KEY. This command is used to set and unset private
338 keys for channels, set and unset private keys for private messages
339 with remote clients and to send key agreement requests and
340 negotiate the key agreement protocol with remote client. The
341 key agreement is supported only to negotiate private message keys,
342 it currently cannot be used to negotiate private keys for channels,
343 as it is not convenient for that purpose. */
345 SILC_CLIENT_LCMD_FUNC(key)
347 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
348 SilcClientConnection conn = cmd->conn;
349 SilcClient client = cmd->client;
350 SilcClientEntry client_entry = NULL;
351 SilcChannelEntry channel_entry = NULL;
353 char *nickname = NULL, *server = NULL;
354 int command = 0, port = 0, type = 0;
355 char *hostname = NULL;
356 KeyInternal internal = NULL;
359 silc_say(client, conn,
360 "You are not connected to a server, use /SERVER to connect");
365 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
366 "set|unset|agreement|negotiate [<arguments>]");
371 if (!strcasecmp(cmd->argv[1], "msg"))
373 if (!strcasecmp(cmd->argv[1], "channel"))
377 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
378 "set|unset|agreement|negotiate [<arguments>]");
383 if (cmd->argv[2][0] == '*') {
386 /* Parse the typed nickname. */
387 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
388 silc_say(client, conn, "Bad nickname");
392 /* Find client entry */
393 client_entry = silc_idlist_get_client(client, conn, nickname,
396 /* Client entry not found, it was requested thus mark this to be
398 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
400 NULL, silc_client_local_command_key,
408 /* Get channel entry */
411 if (cmd->argv[2][0] == '*') {
412 if (!conn->current_channel) {
413 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
416 name = conn->current_channel->channel_name;
421 channel_entry = silc_client_get_channel(client, conn, name);
422 if (!channel_entry) {
423 silc_say(client, conn, "You are not on that channel");
429 if (!strcasecmp(cmd->argv[3], "set")) {
432 if (cmd->argc == 4) {
433 if (curr_key && type == 1 && client_entry) {
434 silc_client_del_private_message_key(client, conn, client_entry);
435 silc_client_add_private_message_key_ske(client, conn, client_entry,
441 if (cmd->argc >= 5) {
442 if (type == 1 && client_entry) {
443 /* Set private message key */
445 silc_client_del_private_message_key(client, conn, client_entry);
448 silc_client_add_private_message_key(client, conn, client_entry,
449 cmd->argv[5], cmd->argv[4],
451 (cmd->argv[4][0] == '*' ?
454 silc_client_add_private_message_key(client, conn, client_entry,
457 (cmd->argv[4][0] == '*' ?
460 /* Send the key to the remote client so that it starts using it
462 silc_client_send_private_message_key(client, conn, client_entry, TRUE);
463 } else if (type == 2) {
464 /* Set private channel key */
465 char *cipher = NULL, *hmac = NULL;
467 if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
468 silc_say(client, conn,
469 "Private key mode is not set on this channel");
474 cipher = cmd->argv[5];
478 if (!silc_client_add_channel_private_key(client, conn, channel_entry,
481 cmd->argv_lens[4])) {
482 silc_say(client, conn, "Could not add channel private key");
492 if (!strcasecmp(cmd->argv[3], "unset")) {
495 if (type == 1 && client_entry) {
496 /* Unset private message key */
497 silc_client_del_private_message_key(client, conn, client_entry);
498 } else if (type == 2) {
499 /* Unset channel key(s) */
500 SilcChannelPrivateKey *keys;
505 silc_client_del_channel_private_keys(client, conn, channel_entry);
508 number = atoi(cmd->argv[4]);
509 keys = silc_client_list_channel_private_keys(client, conn,
515 if (!number || number > keys_count) {
516 silc_client_free_channel_private_keys(keys, keys_count);
520 silc_client_del_channel_private_key(client, conn, channel_entry,
522 silc_client_free_channel_private_keys(keys, keys_count);
530 if (!strcasecmp(cmd->argv[3], "list")) {
534 SilcPrivateMessageKeys keys;
539 keys = silc_client_list_private_message_keys(client, conn,
544 /* list the private message key(s) */
545 if (nickname[0] == '*') {
546 silc_say(client, conn, "Private message keys");
547 silc_say(client, conn,
548 " Client Cipher Key");
549 for (k = 0; k < keys_count; k++) {
550 memset(buf, 0, sizeof(buf));
551 strncat(buf, " ", 2);
552 len = strlen(keys[k].client_entry->nickname);
553 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
555 for (i = 0; i < 30 - len; i++)
559 len = strlen(keys[k].cipher);
560 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
562 for (i = 0; i < 14 - len; i++)
567 strcat(buf, "<hidden>");
569 strcat(buf, "*generated*");
571 silc_say(client, conn, "%s", buf);
574 silc_say(client, conn, "Private message key",
575 client_entry->nickname);
576 silc_say(client, conn,
577 " Client Cipher Key");
578 for (k = 0; k < keys_count; k++) {
579 if (keys[k].client_entry != client_entry)
582 memset(buf, 0, sizeof(buf));
583 strncat(buf, " ", 2);
584 len = strlen(keys[k].client_entry->nickname);
585 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
587 for (i = 0; i < 30 - len; i++)
591 len = strlen(keys[k].cipher);
592 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
594 for (i = 0; i < 14 - len; i++)
599 strcat(buf, "<hidden>");
601 strcat(buf, "*generated*");
603 silc_say(client, conn, "%s", buf);
607 silc_client_free_private_message_keys(keys, keys_count);
608 } else if (type == 2) {
609 SilcChannelPrivateKey *keys;
614 keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
619 silc_say(client, conn, "Channel %s private keys",
620 channel_entry->channel_name);
621 silc_say(client, conn,
623 for (k = 0; k < keys_count; k++) {
624 memset(buf, 0, sizeof(buf));
625 strncat(buf, " ", 2);
627 len = strlen(keys[k]->cipher->cipher->name);
628 strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
630 for (i = 0; i < 16 - len; i++)
634 len = strlen(keys[k]->hmac->hmac->name);
635 strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
637 for (i = 0; i < 16 - len; i++)
641 strcat(buf, "<hidden>");
643 silc_say(client, conn, "%s", buf);
646 silc_client_free_channel_private_keys(keys, keys_count);
652 /* Send command is used to send key agreement */
653 if (!strcasecmp(cmd->argv[3], "agreement")) {
657 hostname = cmd->argv[4];
659 port = atoi(cmd->argv[5]);
661 internal = silc_calloc(1, sizeof(*internal));
662 internal->type = type;
665 /* Start command is used to start key agreement (after receiving the
666 key_agreement client operation). */
667 if (!strcasecmp(cmd->argv[3], "negotiate")) {
671 hostname = cmd->argv[4];
673 port = atoi(cmd->argv[5]);
675 internal = silc_calloc(1, sizeof(*internal));
676 internal->type = type;
680 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
681 "set|unset|agreement|negotiate [<arguments>]");
685 if (command == 4 && client_entry) {
686 silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
687 silc_client_send_key_agreement(client, conn, client_entry, hostname,
688 port, 120, keyagr_completion, internal);
692 if (command == 5 && client_entry) {
693 silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
694 silc_client_perform_key_agreement(client, conn, client_entry, hostname,
695 port, keyagr_completion, internal);
704 silc_client_command_free(cmd);
707 /* Sends an action to the channel. Equals CTCP's ACTION (IRC's /ME)
710 SILC_CLIENT_LCMD_FUNC(me)
712 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
713 SilcClientConnection conn = cmd->conn;
714 SilcClient client = cmd->client;
715 SilcChannelEntry channel_entry;
719 silc_say(client, conn,
720 "You are not connected to a server, use /SERVER to connect");
725 silc_say(client, conn, "Usage: /ME <channel> <action message>");
729 if (cmd->argv[1][0] == '*') {
730 if (!conn->current_channel) {
731 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
734 name = conn->current_channel->channel_name;
739 channel_entry = silc_client_get_channel(client, conn, name);
740 if (!channel_entry) {
741 silc_say(client, conn, "You are not on that channel");
745 /* Send the action message */
746 silc_client_send_channel_message(client, conn, channel_entry, NULL,
747 SILC_MESSAGE_FLAG_ACTION,
748 cmd->argv[2], cmd->argv_lens[2], TRUE);
750 silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
753 silc_client_command_free(cmd);
756 /* Sends an notice to the channel. */
758 SILC_CLIENT_LCMD_FUNC(notice)
760 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
761 SilcClientConnection conn = cmd->conn;
762 SilcClient client = cmd->client;
763 SilcChannelEntry channel_entry;
767 silc_say(client, conn,
768 "You are not connected to a server, use /SERVER to connect");
773 silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
777 if (cmd->argv[1][0] == '*') {
778 if (!conn->current_channel) {
779 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
782 name = conn->current_channel->channel_name;
787 channel_entry = silc_client_get_channel(client, conn, name);
788 if (!channel_entry) {
789 silc_say(client, conn, "You are not on that channel");
793 /* Send the action message */
794 silc_client_send_channel_message(client, conn, channel_entry, NULL,
795 SILC_MESSAGE_FLAG_NOTICE,
796 cmd->argv[2], cmd->argv_lens[2], TRUE);
798 silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
801 silc_client_command_free(cmd);