5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 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.
21 #include "clientincludes.h"
23 /* Prints a message with three star (*) sign before the actual message
24 on the current output window. This is used to print command outputs
25 and error messages. */
27 void silc_say(SilcClient client, SilcClientConnection conn,
32 SilcClientInternal app = (SilcClientInternal)client->application;
34 memset(message, 0, sizeof(message));
35 strncat(message, "\n*** ", 5);
38 vsprintf(message + 5, msg, vp);
41 /* Print the message */
42 silc_print_to_window(app->screen->output_win[0], message);
45 /* Message for a channel. The `sender' is the nickname of the sender
46 received in the packet. The `channel_name' is the name of the channel. */
48 void silc_channel_message(SilcClient client, SilcClientConnection conn,
49 SilcClientEntry sender, SilcChannelEntry channel
52 /* Message from client */
53 if (conn && !strcmp(conn->current_channel->channel_name,
54 channel->channel_name))
55 silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]",
58 silc_print(client, "<%s:%s> %s", sender ? sender->nickname : "[<unknown>]",
59 channel->channel_name, msg);
62 /* Private message to the client. The `sender' is the nickname of the
63 sender received in the packet. */
65 void silc_private_message(SilcClient client, SilcClientConnection conn,
66 SilcClientEntry sender, char *msg)
68 silc_print(client, "*%s* %s", sender->nickname, msg);
72 /* Notify message to the client. The notify arguments are sent in the
73 same order as servers sends them. The arguments are same as received
74 from the server except for ID's. If ID is received application receives
75 the corresponding entry to the ID. For example, if Client ID is received
76 application receives SilcClientEntry. Also, if the notify type is
77 for channel the channel entry is sent to application (even if server
80 void silc_notify(SilcClient client, SilcClientConnection conn,
81 SilcNotifyType type, ...)
83 SilcClientInternal app = (SilcClientInternal)client->application;
86 SilcClientEntry client_entry, client_entry2;
87 SilcChannelEntry channel_entry;
93 memset(message, 0, sizeof(message));
95 /* Get arguments (defined by protocol in silc-pp-01 -draft) */
97 case SILC_NOTIFY_TYPE_NONE:
98 tmp = va_arg(vp, char *);
101 strcpy(message, tmp);
104 case SILC_NOTIFY_TYPE_INVITE:
105 client_entry = va_arg(vp, SilcClientEntry);
106 channel_entry = va_arg(vp, SilcChannelEntry);
107 snprintf(message, sizeof(message), "%s invites you to channel %s",
108 client_entry->nickname, channel_entry->channel_name);
111 case SILC_NOTIFY_TYPE_JOIN:
112 client_entry = va_arg(vp, SilcClientEntry);
113 channel_entry = va_arg(vp, SilcChannelEntry);
114 snprintf(message, sizeof(message), "%s (%s) has joined channel %s",
115 client_entry->nickname, client_entry->username,
116 channel_entry->channel_name);
119 case SILC_NOTIFY_TYPE_LEAVE:
120 client_entry = va_arg(vp, SilcClientEntry);
121 channel_entry = va_arg(vp, SilcChannelEntry);
122 if (client_entry->server)
123 snprintf(message, sizeof(message), "%s@%s has left channel %s",
124 client_entry->nickname, client_entry->server,
125 channel_entry->channel_name);
127 snprintf(message, sizeof(message), "%s has left channel %s",
128 client_entry->nickname, channel_entry->channel_name);
131 case SILC_NOTIFY_TYPE_SIGNOFF:
132 client_entry = va_arg(vp, SilcClientEntry);
133 tmp = va_arg(vp, char *);
134 if (client_entry->server)
135 snprintf(message, sizeof(message), "Signoff: %s@%s %s%s%s",
136 client_entry->nickname, client_entry->server,
137 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
139 snprintf(message, sizeof(message), "Signoff: %s %s%s%s",
140 client_entry->nickname,
141 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
144 case SILC_NOTIFY_TYPE_TOPIC_SET:
145 client_entry = va_arg(vp, SilcClientEntry);
146 tmp = va_arg(vp, char *);
147 channel_entry = va_arg(vp, SilcChannelEntry);
148 if (client_entry->server)
149 snprintf(message, sizeof(message), "%s@%s set topic on %s: %s",
150 client_entry->nickname, client_entry->server,
151 channel_entry->channel_name, tmp);
153 snprintf(message, sizeof(message), "%s set topic on %s: %s",
154 client_entry->nickname, channel_entry->channel_name, tmp);
157 case SILC_NOTIFY_TYPE_NICK_CHANGE:
158 client_entry = va_arg(vp, SilcClientEntry);
159 client_entry2 = va_arg(vp, SilcClientEntry);
160 if (client_entry->server && client_entry2->server)
161 snprintf(message, sizeof(message), "%s@%s is known as %s@%s",
162 client_entry->nickname, client_entry->server,
163 client_entry2->nickname, client_entry2->server);
165 snprintf(message, sizeof(message), "%s is known as %s",
166 client_entry->nickname, client_entry2->nickname);
169 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
170 client_entry = va_arg(vp, SilcClientEntry);
171 tmp = silc_client_chmode(va_arg(vp, unsigned int));
172 channel_entry = va_arg(vp, SilcChannelEntry);
174 snprintf(message, sizeof(message), "%s changed channel mode to +%s",
175 client_entry->nickname, tmp);
177 snprintf(message, sizeof(message), "%s removed all channel modes",
178 client_entry->nickname);
179 if (app->screen->bottom_line->channel_mode)
180 silc_free(app->screen->bottom_line->channel_mode);
181 app->screen->bottom_line->channel_mode = tmp;
182 silc_screen_print_bottom_line(app->screen, 0);
185 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
186 client_entry = va_arg(vp, SilcClientEntry);
187 tmp_int = va_arg(vp, unsigned int);
188 tmp = silc_client_chumode(tmp_int);
189 client_entry2 = va_arg(vp, SilcClientEntry);
190 channel_entry = va_arg(vp, SilcChannelEntry);
192 snprintf(message, sizeof(message), "%s changed %s's mode to +%s",
193 client_entry->nickname, client_entry2->nickname, tmp);
195 snprintf(message, sizeof(message), "%s removed %s's modes",
196 client_entry->nickname, client_entry2->nickname);
197 if (client_entry2 == conn->local_entry) {
198 if (app->screen->bottom_line->mode)
199 silc_free(app->screen->bottom_line->mode);
200 app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
201 silc_screen_print_bottom_line(app->screen, 0);
206 case SILC_NOTIFY_TYPE_MOTD:
210 tmp = va_arg(vp, unsigned char *);
214 if (tmp[i++] == '\n') {
215 memset(line, 0, sizeof(line));
216 strncat(line, tmp, i - 1);
219 silc_say(client, conn, "%s", line);
229 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
232 case SILC_NOTIFY_TYPE_KICKED:
233 client_entry = va_arg(vp, SilcClientEntry);
234 tmp = va_arg(vp, char *);
235 channel_entry = va_arg(vp, SilcChannelEntry);
237 if (client_entry == conn->local_entry) {
238 snprintf(message, sizeof(message),
239 "You have been kicked off channel %s %s%s%s",
240 conn->current_channel->channel_name,
241 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
243 snprintf(message, sizeof(message),
244 "%s%s%s has been kicked off channel %s %s%s%s",
245 client_entry->nickname,
246 client_entry->server ? "@" : "",
247 client_entry->server ? client_entry->server : "",
248 conn->current_channel->channel_name,
249 tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
257 silc_print(client, "*** %s", message);
260 /* Command handler. This function is called always in the command function.
261 If error occurs it will be called as well. `conn' is the associated
262 client connection. `cmd_context' is the command context that was
263 originally sent to the command. `success' is FALSE if error occured
264 during command. `command' is the command being processed. It must be
265 noted that this is not reply from server. This is merely called just
266 after application has called the command. Just to tell application
267 that the command really was processed. */
269 void silc_command(SilcClient client, SilcClientConnection conn,
270 SilcClientCommandContext cmd_context, int success,
273 SilcClientInternal app = (SilcClientInternal)client->application;
281 case SILC_COMMAND_QUIT:
282 app->screen->bottom_line->channel = NULL;
283 silc_screen_print_bottom_line(app->screen, 0);
286 case SILC_COMMAND_LEAVE:
288 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
289 app->screen->bottom_line->channel = NULL;
290 silc_screen_print_bottom_line(app->screen, 0);
298 /* We've resolved all clients we don't know about, now just print the
299 users from the channel on the screen. */
301 void silc_client_show_users(SilcClient client,
302 SilcClientConnection conn,
303 SilcClientEntry *clients,
304 unsigned int clients_count,
307 SilcChannelEntry channel = (SilcChannelEntry)context;
309 int k = 0, len1 = 0, len2 = 0;
310 char *name_list = NULL;
315 silc_list_start(channel->clients);
316 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
317 char *m, *n = chu->client->nickname;
324 name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
326 m = silc_client_chumode_char(chu->mode);
328 memcpy(name_list + (len1 - len2), m, strlen(m));
333 memcpy(name_list + (len1 - len2), n, len2);
336 if (k == silc_list_count(channel->clients) - 1)
338 memcpy(name_list + len1, " ", 1);
343 client->ops->say(client, conn, "Users on %s: %s", channel->channel_name,
345 silc_free(name_list);
348 /* Command reply handler. This function is called always in the command reply
349 function. If error occurs it will be called as well. Normal scenario
350 is that it will be called after the received command data has been parsed
351 and processed. The function is used to pass the received command data to
354 `conn' is the associated client connection. `cmd_payload' is the command
355 payload data received from server and it can be ignored. It is provided
356 if the application would like to re-parse the received command data,
357 however, it must be noted that the data is parsed already by the library
358 thus the payload can be ignored. `success' is FALSE if error occured.
359 In this case arguments are not sent to the application. `command' is the
360 command reply being processed. The function has variable argument list
361 and each command defines the number and type of arguments it passes to the
362 application (on error they are not sent). */
364 void silc_command_reply(SilcClient client, SilcClientConnection conn,
365 SilcCommandPayload cmd_payload, int success,
366 SilcCommand command, SilcCommandStatus status, ...)
368 SilcClientInternal app = (SilcClientInternal)client->application;
372 va_start(vp, status);
376 case SILC_COMMAND_WHOIS:
378 char buf[1024], *nickname, *username, *realname;
382 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
384 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
387 client->ops->say(client, conn, "%s: %s", tmp,
388 silc_client_command_status_message(status));
390 client->ops->say(client, conn, "%s",
391 silc_client_command_status_message(status));
398 (void)va_arg(vp, SilcClientEntry);
399 nickname = va_arg(vp, char *);
400 username = va_arg(vp, char *);
401 realname = va_arg(vp, char *);
402 (void)va_arg(vp, void *);
403 idle = va_arg(vp, unsigned int);
405 memset(buf, 0, sizeof(buf));
408 len = strlen(nickname);
409 strncat(buf, nickname, len);
410 strncat(buf, " is ", 4);
414 strncat(buf, username, strlen(username));
418 strncat(buf, " (", 2);
419 strncat(buf, realname, strlen(realname));
420 strncat(buf, ")", 1);
423 client->ops->say(client, conn, "%s", buf);
424 if (idle && nickname)
425 client->ops->say(client, conn, "%s has been idle %d %s",
427 idle > 60 ? (idle / 60) : idle,
428 idle > 60 ? "minutes" : "seconds");
432 case SILC_COMMAND_WHOWAS:
434 char buf[1024], *nickname, *username, *realname;
437 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
439 tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
442 client->ops->say(client, conn, "%s: %s", tmp,
443 silc_client_command_status_message(status));
445 client->ops->say(client, conn, "%s",
446 silc_client_command_status_message(status));
453 (void)va_arg(vp, SilcClientEntry);
454 nickname = va_arg(vp, char *);
455 username = va_arg(vp, char *);
456 realname = va_arg(vp, char *);
458 memset(buf, 0, sizeof(buf));
461 len = strlen(nickname);
462 strncat(buf, nickname, len);
463 strncat(buf, " was ", 5);
467 strncat(buf, username, strlen(nickname));
471 strncat(buf, " (", 2);
472 strncat(buf, realname, strlen(realname));
473 strncat(buf, ")", 1);
476 client->ops->say(client, conn, "%s", buf);
480 case SILC_COMMAND_JOIN:
484 SilcBuffer client_id_list;
485 unsigned int list_count;
486 SilcChannelEntry channel;
491 app->screen->bottom_line->channel = va_arg(vp, char *);
492 channel = va_arg(vp, SilcChannelEntry);
493 mode = va_arg(vp, unsigned int);
494 (void)va_arg(vp, unsigned int);
495 (void)va_arg(vp, unsigned char *);
496 (void)va_arg(vp, unsigned char *);
497 (void)va_arg(vp, unsigned char *);
498 topic = va_arg(vp, char *);
499 (void)va_arg(vp, unsigned char *);
500 list_count = va_arg(vp, unsigned int);
501 client_id_list = va_arg(vp, SilcBuffer);
504 client->ops->say(client, conn, "Topic for %s: %s",
505 app->screen->bottom_line->channel, topic);
507 app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
508 silc_screen_print_bottom_line(app->screen, 0);
510 /* Resolve the client information */
511 silc_client_get_clients_by_list(client, conn, list_count,
513 silc_client_show_users, channel);
517 case SILC_COMMAND_NICK:
519 SilcClientEntry entry;
524 entry = va_arg(vp, SilcClientEntry);
525 silc_say(client, conn, "Your current nickname is %s", entry->nickname);
526 app->screen->bottom_line->nickname = entry->nickname;
527 silc_screen_print_bottom_line(app->screen, 0);
531 case SILC_COMMAND_USERS:
535 silc_list_start(conn->current_channel->clients);
536 while ((chu = silc_list_get(conn->current_channel->clients))
538 if (chu->client == conn->local_entry) {
539 if (app->screen->bottom_line->mode)
540 silc_free(app->screen->bottom_line->mode);
541 app->screen->bottom_line->mode =
542 silc_client_chumode_char(chu->mode);
543 silc_screen_print_bottom_line(app->screen, 0);
551 /* Called to indicate that connection was either successfully established
552 or connecting failed. This is also the first time application receives
553 the SilcClientConnection objecet which it should save somewhere. */
555 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
557 SilcClientInternal app = (SilcClientInternal)client->application;
560 app->screen->bottom_line->connection = conn->remote_host;
561 silc_screen_print_bottom_line(app->screen, 0);
566 /* Called to indicate that connection was disconnected to the server. */
568 void silc_disconnect(SilcClient client, SilcClientConnection conn)
570 SilcClientInternal app = (SilcClientInternal)client->application;
572 app->screen->bottom_line->connection = NULL;
573 silc_screen_print_bottom_line(app->screen, 0);
577 /* Asks passphrase from user on the input line. */
579 unsigned char *silc_ask_passphrase(SilcClient client,
580 SilcClientConnection conn)
582 SilcClientInternal app = (SilcClientInternal)conn->client->application;
583 char pass1[256], pass2[256];
590 wattroff(app->screen->input_win, A_INVIS);
591 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
592 wattron(app->screen->input_win, A_INVIS);
595 memset(pass1, 0, sizeof(pass1));
596 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
598 /* Print retype prompt */
599 wattroff(app->screen->input_win, A_INVIS);
600 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
601 wattron(app->screen->input_win, A_INVIS);
604 memset(pass2, 0, sizeof(pass2));
605 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
607 if (!strncmp(pass1, pass2, strlen(pass2)))
613 ret = silc_calloc(strlen(pass1), sizeof(char));
614 memcpy(ret, pass1, strlen(pass1));
616 memset(pass1, 0, sizeof(pass1));
617 memset(pass2, 0, sizeof(pass2));
619 wattroff(app->screen->input_win, A_INVIS);
620 silc_screen_input_reset(app->screen);
625 /* Verifies received public key. If user decides to trust the key it is
626 saved as trusted server key for later use. If user does not trust the
627 key this returns FALSE. */
629 int silc_verify_server_key(SilcClient client,
630 SilcClientConnection conn,
631 unsigned char *pk, unsigned int pk_len,
632 SilcSKEPKType pk_type)
634 SilcSocketConnection sock = conn->sock;
637 char *hostname, *fingerprint;
641 hostname = sock->hostname ? sock->hostname : sock->ip;
643 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
644 silc_say(client, conn, "We don't support server %s key type", hostname);
648 pw = getpwuid(getuid());
652 memset(filename, 0, sizeof(filename));
653 memset(file, 0, sizeof(file));
654 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
656 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
659 /* Check wheter this key already exists */
660 if (stat(filename, &st) < 0) {
662 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
663 silc_say(client, conn, "Received server %s public key", hostname);
664 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
665 silc_say(client, conn, "%s", fingerprint);
666 silc_free(fingerprint);
668 /* Ask user to verify the key and save it */
669 if (silc_client_ask_yes_no(client,
670 "Would you like to accept the key (y/n)? "))
672 /* Save the key for future checking */
673 silc_pkcs_save_public_key_data(filename, pk, pk_len,
678 /* The key already exists, verify it. */
679 SilcPublicKey public_key;
680 unsigned char *encpk;
681 unsigned int encpk_len;
683 /* Load the key file */
684 if (!silc_pkcs_load_public_key(filename, &public_key,
686 if (!silc_pkcs_load_public_key(filename, &public_key,
687 SILC_PKCS_FILE_BIN)) {
688 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
689 silc_say(client, conn, "Received server %s public key", hostname);
690 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
691 silc_say(client, conn, "%s", fingerprint);
692 silc_free(fingerprint);
693 silc_say(client, conn, "Could not load your local copy of the server %s key",
695 if (silc_client_ask_yes_no(client,
696 "Would you like to accept the key anyway (y/n)? "))
698 /* Save the key for future checking */
700 silc_pkcs_save_public_key_data(filename, pk, pk_len,
708 /* Encode the key data */
709 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
711 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
712 silc_say(client, conn, "Received server %s public key", hostname);
713 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
714 silc_say(client, conn, "%s", fingerprint);
715 silc_free(fingerprint);
716 silc_say(client, conn, "Your local copy of the server %s key is malformed",
718 if (silc_client_ask_yes_no(client,
719 "Would you like to accept the key anyway (y/n)? "))
721 /* Save the key for future checking */
723 silc_pkcs_save_public_key_data(filename, pk, pk_len,
731 if (memcmp(encpk, pk, encpk_len)) {
732 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
733 silc_say(client, conn, "Received server %s public key", hostname);
734 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
735 silc_say(client, conn, "%s", fingerprint);
736 silc_free(fingerprint);
737 silc_say(client, conn, "Server %s key does not match with your local copy",
739 silc_say(client, conn, "It is possible that the key has expired or changed");
740 silc_say(client, conn, "It is also possible that some one is performing "
741 "man-in-the-middle attack");
743 /* Ask user to verify the key and save it */
744 if (silc_client_ask_yes_no(client,
745 "Would you like to accept the key anyway (y/n)? "))
747 /* Save the key for future checking */
749 silc_pkcs_save_public_key_data(filename, pk, pk_len,
754 silc_say(client, conn, "Will not accept server %s key", hostname);
758 /* Local copy matched */
762 silc_say(client, conn, "Will not accept server %s key", hostname);
766 /* Find authentication method and authentication data by hostname and
767 port. The hostname may be IP address as well. The found authentication
768 method and authentication data is returned to `auth_meth', `auth_data'
769 and `auth_data_len'. The function returns TRUE if authentication method
770 is found and FALSE if not. `conn' may be NULL. */
772 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
773 char *hostname, unsigned short port,
774 SilcProtocolAuthMeth *auth_meth,
775 unsigned char **auth_data,
776 unsigned int *auth_data_len)
778 SilcClientInternal app = (SilcClientInternal)client->application;
780 if (app->config->conns) {
781 SilcClientConfigSectionConnection *conn = NULL;
783 /* Check if we find a match from user configured connections */
784 conn = silc_client_config_find_connection(app->config,
788 /* Match found. Use the configured authentication method */
789 *auth_meth = conn->auth_meth;
791 if (conn->auth_data) {
792 *auth_data = strdup(conn->auth_data);
793 *auth_data_len = strlen(conn->auth_data);
803 /* Notifies application that failure packet was received. This is called
804 if there is some protocol active in the client. The `protocol' is the
805 protocol context. The `failure' is opaque pointer to the failure
806 indication. Note, that the `failure' is protocol dependant and application
807 must explicitly cast it to correct type. Usually `failure' is 32 bit
808 failure type (see protocol specs for all protocol failure types). */
810 void silc_failure(SilcClient client, SilcClientConnection conn,
811 SilcProtocol protocol, void *failure)
816 /* Asks whether the user would like to perform the key agreement protocol.
817 This is called after we have received an key agreement packet or an
818 reply to our key agreement packet. This returns TRUE if the user wants
819 the library to perform the key agreement protocol and FALSE if it is not
820 desired (application may start it later by calling the function
821 silc_client_perform_key_agreement). */
823 int silc_key_agreement(SilcClient client, SilcClientConnection conn,
824 SilcClientEntry client_entry, char *hostname,
826 SilcKeyAgreementCallback *completion,
833 /* SILC client operations */
834 SilcClientOperations ops = {
836 channel_message: silc_channel_message,
837 private_message: silc_private_message,
839 command: silc_command,
840 command_reply: silc_command_reply,
841 connect: silc_connect,
842 disconnect: silc_disconnect,
843 get_auth_method: silc_get_auth_method,
844 verify_server_key: silc_verify_server_key,
845 ask_passphrase: silc_ask_passphrase,
846 failure: silc_failure,
847 key_agreement: silc_key_agreement,