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;
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;
221 unsigned char modebuf[4];
222 SilcBuffer idp, buffer;
225 silc_say(client, conn,
226 "You are not connected to a server, use /SERVER to connect");
230 if (cmd->argc == 1) {
231 conn->local_entry->mode &= ~SILC_UMODE_GONE;
234 silc_free(conn->away->away);
235 silc_free(conn->away);
237 app->screen->bottom_line->away = FALSE;
239 silc_say(client, conn, "Away message removed");
240 silc_screen_print_bottom_line(app->screen, 0);
243 conn->local_entry->mode |= SILC_UMODE_GONE;
246 silc_free(conn->away->away);
248 conn->away = silc_calloc(1, sizeof(*conn->away));
250 app->screen->bottom_line->away = TRUE;
251 conn->away->away = strdup(cmd->argv[1]);
253 silc_say(client, conn, "Away message set: %s", conn->away->away);
254 silc_screen_print_bottom_line(app->screen, 0);
257 /* Send the UMODE command to se myself as gone */
258 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
259 SILC_PUT32_MSB(conn->local_entry->mode, modebuf);
260 buffer = silc_command_payload_encode_va(SILC_COMMAND_UMODE,
261 ++conn->cmd_ident, 2,
262 1, idp->data, idp->len,
263 2, modebuf, sizeof(modebuf));
264 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
265 NULL, 0, NULL, NULL, buffer->data,
267 silc_buffer_free(buffer);
268 silc_buffer_free(idp);
271 silc_client_command_free(cmd);
275 int type; /* 1 = msg, 2 = channel */
278 static SilcSKEKeyMaterial *curr_key = NULL;
280 /* Key agreement callback that is called after the key agreement protocol
281 has been performed. This is called also if error occured during the
282 key agreement protocol. The `key' is the allocated key material and
283 the caller is responsible of freeing it. The `key' is NULL if error
284 has occured. The application can freely use the `key' to whatever
285 purpose it needs. See lib/silcske/silcske.h for the definition of
286 the SilcSKEKeyMaterial structure. */
288 static void keyagr_completion(SilcClient client,
289 SilcClientConnection conn,
290 SilcClientEntry client_entry,
291 SilcKeyAgreementStatus status,
292 SilcSKEKeyMaterial *key,
295 KeyInternal i = (KeyInternal)context;
300 case SILC_KEY_AGREEMENT_OK:
301 silc_say(client, conn, "Key agreement compeleted successfully with %s",
302 client_entry->nickname);;
305 /* Set the private key for this client */
306 silc_client_del_private_message_key(client, conn, client_entry);
307 silc_client_add_private_message_key_ske(client, conn, client_entry,
309 silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
310 silc_ske_free_key_material(key);
315 case SILC_KEY_AGREEMENT_ERROR:
316 silc_say(client, conn, "Error occured during key agreement with %s",
317 client_entry->nickname);
320 case SILC_KEY_AGREEMENT_FAILURE:
321 silc_say(client, conn, "The key agreement failed with %s",
322 client_entry->nickname);
325 case SILC_KEY_AGREEMENT_TIMEOUT:
326 silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
327 client_entry->nickname);
338 /* Local command KEY. This command is used to set and unset private
339 keys for channels, set and unset private keys for private messages
340 with remote clients and to send key agreement requests and
341 negotiate the key agreement protocol with remote client. The
342 key agreement is supported only to negotiate private message keys,
343 it currently cannot be used to negotiate private keys for channels,
344 as it is not convenient for that purpose. */
346 SILC_CLIENT_LCMD_FUNC(key)
348 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
349 SilcClientConnection conn = cmd->conn;
350 SilcClient client = cmd->client;
351 SilcClientEntry client_entry = NULL;
352 SilcChannelEntry channel_entry = NULL;
354 char *nickname = NULL, *server = NULL;
355 int command = 0, port = 0, type = 0;
356 char *hostname = NULL;
357 KeyInternal internal = NULL;
360 silc_say(client, conn,
361 "You are not connected to a server, use /SERVER to connect");
366 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
367 "set|unset|agreement|negotiate [<arguments>]");
372 if (!strcasecmp(cmd->argv[1], "msg"))
374 if (!strcasecmp(cmd->argv[1], "channel"))
378 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
379 "set|unset|agreement|negotiate [<arguments>]");
384 if (cmd->argv[2][0] == '*') {
387 /* Parse the typed nickname. */
388 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
389 silc_say(client, conn, "Bad nickname");
393 /* Find client entry */
394 client_entry = silc_idlist_get_client(client, conn, nickname,
397 /* Client entry not found, it was requested thus mark this to be
399 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
401 NULL, silc_client_local_command_key,
409 /* Get channel entry */
412 if (cmd->argv[2][0] == '*') {
413 if (!conn->current_channel) {
414 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
417 name = conn->current_channel->channel_name;
422 channel_entry = silc_client_get_channel(client, conn, name);
423 if (!channel_entry) {
424 silc_say(client, conn, "You are not on that channel");
430 if (!strcasecmp(cmd->argv[3], "set")) {
433 if (cmd->argc == 4) {
434 if (curr_key && type == 1 && client_entry) {
435 silc_client_del_private_message_key(client, conn, client_entry);
436 silc_client_add_private_message_key_ske(client, conn, client_entry,
442 if (cmd->argc >= 5) {
443 if (type == 1 && client_entry) {
444 /* Set private message key */
446 silc_client_del_private_message_key(client, conn, client_entry);
449 silc_client_add_private_message_key(client, conn, client_entry,
450 cmd->argv[5], cmd->argv[4],
452 (cmd->argv[4][0] == '*' ?
455 silc_client_add_private_message_key(client, conn, client_entry,
458 (cmd->argv[4][0] == '*' ?
461 /* Send the key to the remote client so that it starts using it
463 silc_client_send_private_message_key(client, conn, client_entry, TRUE);
464 } else if (type == 2) {
465 /* Set private channel key */
466 char *cipher = NULL, *hmac = NULL;
468 if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
469 silc_say(client, conn,
470 "Private key mode is not set on this channel");
475 cipher = cmd->argv[5];
479 if (!silc_client_add_channel_private_key(client, conn, channel_entry,
482 cmd->argv_lens[4])) {
483 silc_say(client, conn, "Could not add channel private key");
493 if (!strcasecmp(cmd->argv[3], "unset")) {
496 if (type == 1 && client_entry) {
497 /* Unset private message key */
498 silc_client_del_private_message_key(client, conn, client_entry);
499 } else if (type == 2) {
500 /* Unset channel key(s) */
501 SilcChannelPrivateKey *keys;
506 silc_client_del_channel_private_keys(client, conn, channel_entry);
509 number = atoi(cmd->argv[4]);
510 keys = silc_client_list_channel_private_keys(client, conn,
516 if (!number || number > keys_count) {
517 silc_client_free_channel_private_keys(keys, keys_count);
521 silc_client_del_channel_private_key(client, conn, channel_entry,
523 silc_client_free_channel_private_keys(keys, keys_count);
531 if (!strcasecmp(cmd->argv[3], "list")) {
535 SilcPrivateMessageKeys keys;
540 keys = silc_client_list_private_message_keys(client, conn,
545 /* list the private message key(s) */
546 if (nickname[0] == '*') {
547 silc_say(client, conn, "Private message keys");
548 silc_say(client, conn,
549 " Client Cipher Key");
550 for (k = 0; k < keys_count; k++) {
551 memset(buf, 0, sizeof(buf));
552 strncat(buf, " ", 2);
553 len = strlen(keys[k].client_entry->nickname);
554 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
556 for (i = 0; i < 30 - len; i++)
560 len = strlen(keys[k].cipher);
561 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
563 for (i = 0; i < 14 - len; i++)
568 strcat(buf, "<hidden>");
570 strcat(buf, "*generated*");
572 silc_say(client, conn, "%s", buf);
575 silc_say(client, conn, "Private message key",
576 client_entry->nickname);
577 silc_say(client, conn,
578 " Client Cipher Key");
579 for (k = 0; k < keys_count; k++) {
580 if (keys[k].client_entry != client_entry)
583 memset(buf, 0, sizeof(buf));
584 strncat(buf, " ", 2);
585 len = strlen(keys[k].client_entry->nickname);
586 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
588 for (i = 0; i < 30 - len; i++)
592 len = strlen(keys[k].cipher);
593 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
595 for (i = 0; i < 14 - len; i++)
600 strcat(buf, "<hidden>");
602 strcat(buf, "*generated*");
604 silc_say(client, conn, "%s", buf);
608 silc_client_free_private_message_keys(keys, keys_count);
609 } else if (type == 2) {
610 SilcChannelPrivateKey *keys;
615 keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
620 silc_say(client, conn, "Channel %s private keys",
621 channel_entry->channel_name);
622 silc_say(client, conn,
624 for (k = 0; k < keys_count; k++) {
625 memset(buf, 0, sizeof(buf));
626 strncat(buf, " ", 2);
628 len = strlen(keys[k]->cipher->cipher->name);
629 strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
631 for (i = 0; i < 16 - len; i++)
635 len = strlen(keys[k]->hmac->hmac->name);
636 strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
638 for (i = 0; i < 16 - len; i++)
642 strcat(buf, "<hidden>");
644 silc_say(client, conn, "%s", buf);
647 silc_client_free_channel_private_keys(keys, keys_count);
653 /* Send command is used to send key agreement */
654 if (!strcasecmp(cmd->argv[3], "agreement")) {
658 hostname = cmd->argv[4];
660 port = atoi(cmd->argv[5]);
662 internal = silc_calloc(1, sizeof(*internal));
663 internal->type = type;
666 /* Start command is used to start key agreement (after receiving the
667 key_agreement client operation). */
668 if (!strcasecmp(cmd->argv[3], "negotiate")) {
672 hostname = cmd->argv[4];
674 port = atoi(cmd->argv[5]);
676 internal = silc_calloc(1, sizeof(*internal));
677 internal->type = type;
681 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
682 "set|unset|agreement|negotiate [<arguments>]");
686 if (command == 4 && client_entry) {
687 silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
688 silc_client_send_key_agreement(client, conn, client_entry, hostname,
689 port, 120, keyagr_completion, internal);
693 if (command == 5 && client_entry) {
694 silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
695 silc_client_perform_key_agreement(client, conn, client_entry, hostname,
696 port, keyagr_completion, internal);
705 silc_client_command_free(cmd);
708 /* Sends an action to the channel. Equals CTCP's ACTION (IRC's /ME)
711 SILC_CLIENT_LCMD_FUNC(me)
713 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
714 SilcClientConnection conn = cmd->conn;
715 SilcClient client = cmd->client;
716 SilcChannelEntry channel_entry;
720 silc_say(client, conn,
721 "You are not connected to a server, use /SERVER to connect");
726 silc_say(client, conn, "Usage: /ME <channel> <action message>");
730 if (cmd->argv[1][0] == '*') {
731 if (!conn->current_channel) {
732 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
735 name = conn->current_channel->channel_name;
740 channel_entry = silc_client_get_channel(client, conn, name);
741 if (!channel_entry) {
742 silc_say(client, conn, "You are not on that channel");
746 /* Send the action message */
747 silc_client_send_channel_message(client, conn, channel_entry, NULL,
748 SILC_MESSAGE_FLAG_ACTION,
749 cmd->argv[2], cmd->argv_lens[2], TRUE);
751 silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
754 silc_client_command_free(cmd);
757 /* Sends an notice to the channel. */
759 SILC_CLIENT_LCMD_FUNC(notice)
761 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
762 SilcClientConnection conn = cmd->conn;
763 SilcClient client = cmd->client;
764 SilcChannelEntry channel_entry;
768 silc_say(client, conn,
769 "You are not connected to a server, use /SERVER to connect");
774 silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
778 if (cmd->argv[1][0] == '*') {
779 if (!conn->current_channel) {
780 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
783 name = conn->current_channel->channel_name;
788 channel_entry = silc_client_get_channel(client, conn, name);
789 if (!channel_entry) {
790 silc_say(client, conn, "You are not on that channel");
794 /* Send the action message */
795 silc_client_send_channel_message(client, conn, channel_entry, NULL,
796 SILC_MESSAGE_FLAG_NOTICE,
797 cmd->argv[2], cmd->argv_lens[2], TRUE);
799 silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
802 silc_client_command_free(cmd);