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;
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 if (!silc_client_ask_yes_no(client,
306 "Would you like to use the key with private messages (y/n)? ")) {
307 silc_say(client, conn, "You can set the key material into use later by giving /KEY msg set command");
312 /* Set the private key for this client */
313 silc_client_del_private_message_key(client, conn, client_entry);
314 silc_client_add_private_message_key_ske(client, conn, client_entry,
316 silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
317 silc_ske_free_key_material(key);
322 case SILC_KEY_AGREEMENT_ERROR:
323 silc_say(client, conn, "Error occured during key agreement with %s",
324 client_entry->nickname);
327 case SILC_KEY_AGREEMENT_FAILURE:
328 silc_say(client, conn, "The key agreement failed with %s",
329 client_entry->nickname);
332 case SILC_KEY_AGREEMENT_TIMEOUT:
333 silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
334 client_entry->nickname);
345 /* Local command KEY. This command is used to set and unset private
346 keys for channels, set and unset private keys for private messages
347 with remote clients and to send key agreement requests and
348 negotiate the key agreement protocol with remote client. The
349 key agreement is supported only to negotiate private message keys,
350 it currently cannot be used to negotiate private keys for channels,
351 as it is not convenient for that purpose. */
353 SILC_CLIENT_LCMD_FUNC(key)
355 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
356 SilcClientConnection conn = cmd->conn;
357 SilcClient client = cmd->client;
358 SilcClientEntry client_entry = NULL;
359 SilcChannelEntry channel_entry = NULL;
360 unsigned int num = 0;
361 char *nickname = NULL, *server = NULL;
362 int command = 0, port = 0, type = 0;
363 char *hostname = NULL;
364 KeyInternal internal = NULL;
367 silc_say(client, conn,
368 "You are not connected to a server, use /SERVER to connect");
373 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
374 "set|unset|agreement|negotiate [<arguments>]");
379 if (!strcasecmp(cmd->argv[1], "msg"))
381 if (!strcasecmp(cmd->argv[1], "channel"))
385 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
386 "set|unset|agreement|negotiate [<arguments>]");
391 if (cmd->argv[2][0] == '*') {
394 /* Parse the typed nickname. */
395 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
396 silc_say(client, conn, "Bad nickname");
400 /* Find client entry */
401 client_entry = silc_idlist_get_client(client, conn, nickname,
404 /* Client entry not found, it was requested thus mark this to be
406 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
408 NULL, silc_client_local_command_key,
416 /* Get channel entry */
419 if (cmd->argv[2][0] == '*') {
420 if (!conn->current_channel) {
421 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
424 name = conn->current_channel->channel_name;
429 channel_entry = silc_client_get_channel(client, conn, name);
430 if (!channel_entry) {
431 silc_say(client, conn, "You are not on that channel");
437 if (!strcasecmp(cmd->argv[3], "set")) {
440 if (cmd->argc == 4) {
441 if (curr_key && type == 1 && client_entry) {
442 silc_client_del_private_message_key(client, conn, client_entry);
443 silc_client_add_private_message_key_ske(client, conn, client_entry,
449 if (cmd->argc >= 5) {
450 if (type == 1 && client_entry) {
451 /* Set private message key */
453 silc_client_del_private_message_key(client, conn, client_entry);
456 silc_client_add_private_message_key(client, conn, client_entry,
457 cmd->argv[5], cmd->argv[4],
459 (cmd->argv[4][0] == '*' ?
462 silc_client_add_private_message_key(client, conn, client_entry,
465 (cmd->argv[4][0] == '*' ?
468 /* Send the key to the remote client so that it starts using it
470 silc_client_send_private_message_key(client, conn, client_entry, TRUE);
471 } else if (type == 2) {
472 /* Set private channel key */
473 char *cipher = NULL, *hmac = NULL;
475 if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
476 silc_say(client, conn,
477 "Private key mode is not set on this channel");
482 cipher = cmd->argv[5];
486 if (!silc_client_add_channel_private_key(client, conn, channel_entry,
489 cmd->argv_lens[4])) {
490 silc_say(client, conn, "Could not add channel private key");
500 if (!strcasecmp(cmd->argv[3], "unset")) {
503 if (type == 1 && client_entry) {
504 /* Unset private message key */
505 silc_client_del_private_message_key(client, conn, client_entry);
506 } else if (type == 2) {
507 /* Unset channel key(s) */
508 SilcChannelPrivateKey *keys;
509 unsigned int keys_count;
513 silc_client_del_channel_private_keys(client, conn, channel_entry);
516 number = atoi(cmd->argv[4]);
517 keys = silc_client_list_channel_private_keys(client, conn,
523 if (!number || number > keys_count) {
524 silc_client_free_channel_private_keys(keys, keys_count);
528 silc_client_del_channel_private_key(client, conn, channel_entry,
530 silc_client_free_channel_private_keys(keys, keys_count);
538 if (!strcasecmp(cmd->argv[3], "list")) {
542 SilcPrivateMessageKeys keys;
543 unsigned int keys_count;
547 keys = silc_client_list_private_message_keys(client, conn,
552 /* list the private message key(s) */
553 if (nickname[0] == '*') {
554 silc_say(client, conn, "Private message keys");
555 silc_say(client, conn,
556 " Client Cipher Key");
557 for (k = 0; k < keys_count; k++) {
558 memset(buf, 0, sizeof(buf));
559 strncat(buf, " ", 2);
560 len = strlen(keys[k].client_entry->nickname);
561 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
563 for (i = 0; i < 30 - len; i++)
567 len = strlen(keys[k].cipher);
568 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
570 for (i = 0; i < 14 - len; i++)
575 strcat(buf, "<hidden>");
577 strcat(buf, "*generated*");
579 silc_say(client, conn, "%s", buf);
582 silc_say(client, conn, "Private message key",
583 client_entry->nickname);
584 silc_say(client, conn,
585 " Client Cipher Key");
586 for (k = 0; k < keys_count; k++) {
587 if (keys[k].client_entry != client_entry)
590 memset(buf, 0, sizeof(buf));
591 strncat(buf, " ", 2);
592 len = strlen(keys[k].client_entry->nickname);
593 strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
595 for (i = 0; i < 30 - len; i++)
599 len = strlen(keys[k].cipher);
600 strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
602 for (i = 0; i < 14 - len; i++)
607 strcat(buf, "<hidden>");
609 strcat(buf, "*generated*");
611 silc_say(client, conn, "%s", buf);
615 silc_client_free_private_message_keys(keys, keys_count);
616 } else if (type == 2) {
617 SilcChannelPrivateKey *keys;
618 unsigned int keys_count;
622 keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
627 silc_say(client, conn, "Channel %s private keys",
628 channel_entry->channel_name);
629 silc_say(client, conn,
631 for (k = 0; k < keys_count; k++) {
632 memset(buf, 0, sizeof(buf));
633 strncat(buf, " ", 2);
635 len = strlen(keys[k]->cipher->cipher->name);
636 strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
638 for (i = 0; i < 16 - len; i++)
642 len = strlen(keys[k]->hmac->hmac->name);
643 strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
645 for (i = 0; i < 16 - len; i++)
649 strcat(buf, "<hidden>");
651 silc_say(client, conn, "%s", buf);
654 silc_client_free_channel_private_keys(keys, keys_count);
660 /* Send command is used to send key agreement */
661 if (!strcasecmp(cmd->argv[3], "agreement")) {
665 hostname = cmd->argv[4];
667 port = atoi(cmd->argv[5]);
669 internal = silc_calloc(1, sizeof(*internal));
670 internal->type = type;
673 /* Start command is used to start key agreement (after receiving the
674 key_agreement client operation). */
675 if (!strcasecmp(cmd->argv[3], "negotiate")) {
679 hostname = cmd->argv[4];
681 port = atoi(cmd->argv[5]);
683 internal = silc_calloc(1, sizeof(*internal));
684 internal->type = type;
688 silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
689 "set|unset|agreement|negotiate [<arguments>]");
693 if (command == 4 && client_entry) {
694 silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
695 silc_client_send_key_agreement(client, conn, client_entry, hostname,
696 port, 120, keyagr_completion, internal);
700 if (command == 5 && client_entry) {
701 silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
702 silc_client_perform_key_agreement(client, conn, client_entry, hostname,
703 port, keyagr_completion, internal);
712 silc_client_command_free(cmd);
715 /* Sends an action to the channel. Equals CTCP's ACTION (IRC's /ME)
718 SILC_CLIENT_LCMD_FUNC(me)
720 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
721 SilcClientConnection conn = cmd->conn;
722 SilcClient client = cmd->client;
723 SilcChannelEntry channel_entry;
727 silc_say(client, conn,
728 "You are not connected to a server, use /SERVER to connect");
733 silc_say(client, conn, "Usage: /ME <channel> <action message>");
737 if (cmd->argv[1][0] == '*') {
738 if (!conn->current_channel) {
739 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
742 name = conn->current_channel->channel_name;
747 channel_entry = silc_client_get_channel(client, conn, name);
748 if (!channel_entry) {
749 silc_say(client, conn, "You are not on that channel");
753 /* Send the action message */
754 silc_client_send_channel_message(client, conn, channel_entry, NULL,
755 SILC_MESSAGE_FLAG_ACTION,
756 cmd->argv[2], cmd->argv_lens[2], TRUE);
758 silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
761 silc_client_command_free(cmd);
764 /* Sends an notice to the channel. */
766 SILC_CLIENT_LCMD_FUNC(notice)
768 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
769 SilcClientConnection conn = cmd->conn;
770 SilcClient client = cmd->client;
771 SilcChannelEntry channel_entry;
775 silc_say(client, conn,
776 "You are not connected to a server, use /SERVER to connect");
781 silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
785 if (cmd->argv[1][0] == '*') {
786 if (!conn->current_channel) {
787 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
790 name = conn->current_channel->channel_name;
795 channel_entry = silc_client_get_channel(client, conn, name);
796 if (!channel_entry) {
797 silc_say(client, conn, "You are not on that channel");
801 /* Send the action message */
802 silc_client_send_channel_message(client, conn, channel_entry, NULL,
803 SILC_MESSAGE_FLAG_NOTICE,
804 cmd->argv[2], cmd->argv_lens[2], TRUE);
806 silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
809 silc_client_command_free(cmd);