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 char *sender, char *channel_name, char *msg)
51 /* Message from client */
52 if (conn && !strcmp(conn->current_channel->channel_name, channel_name))
53 silc_print(client, "<%s> %s", sender, msg);
55 silc_print(client, "<%s:%s> %s", sender, channel_name, msg);
58 /* Private message to the client. The `sender' is the nickname of the
59 sender received in the packet. */
61 void silc_private_message(SilcClient client, SilcClientConnection conn,
62 char *sender, char *msg)
64 silc_print(client, "*%s* %s", sender, msg);
68 /* Notify message to the client. The notify arguments are sent in the
69 same order as servers sends them. The arguments are same as received
70 from the server except for ID's. If ID is received application receives
71 the corresponding entry to the ID. For example, if Client ID is received
72 application receives SilcClientEntry. Also, if the notify type is
73 for channel the channel entry is sent to application (even if server
76 void silc_notify(SilcClient client, SilcClientConnection conn,
77 SilcNotifyType type, ...)
79 SilcClientInternal app = (SilcClientInternal)client->application;
82 SilcClientEntry client_entry, client_entry2;
83 SilcChannelEntry channel_entry;
89 memset(message, 0, sizeof(message));
91 /* Get arguments (defined by protocol in silc-pp-01 -draft) */
93 case SILC_NOTIFY_TYPE_NONE:
94 tmp = va_arg(vp, char *);
100 case SILC_NOTIFY_TYPE_INVITE:
101 client_entry = va_arg(vp, SilcClientEntry);
102 channel_entry = va_arg(vp, SilcChannelEntry);
103 snprintf(message, sizeof(message), "%s invites you to channel %s",
104 client_entry->nickname, channel_entry->channel_name);
107 case SILC_NOTIFY_TYPE_JOIN:
108 client_entry = va_arg(vp, SilcClientEntry);
109 channel_entry = va_arg(vp, SilcChannelEntry);
110 snprintf(message, sizeof(message), "%s (%s) has joined channel %s",
111 client_entry->nickname, client_entry->username,
112 channel_entry->channel_name);
115 case SILC_NOTIFY_TYPE_LEAVE:
116 client_entry = va_arg(vp, SilcClientEntry);
117 channel_entry = va_arg(vp, SilcChannelEntry);
118 if (client_entry->server)
119 snprintf(message, sizeof(message), "%s@%s has left channel %s",
120 client_entry->nickname, client_entry->server,
121 channel_entry->channel_name);
123 snprintf(message, sizeof(message), "%s has left channel %s",
124 client_entry->nickname, channel_entry->channel_name);
127 case SILC_NOTIFY_TYPE_SIGNOFF:
128 client_entry = va_arg(vp, SilcClientEntry);
129 if (client_entry->server)
130 snprintf(message, sizeof(message), "Signoff: %s@%s",
131 client_entry->nickname, client_entry->server);
133 snprintf(message, sizeof(message), "Signoff: %s",
134 client_entry->nickname);
137 case SILC_NOTIFY_TYPE_TOPIC_SET:
138 client_entry = va_arg(vp, SilcClientEntry);
139 tmp = va_arg(vp, char *);
140 channel_entry = va_arg(vp, SilcChannelEntry);
141 if (client_entry->server)
142 snprintf(message, sizeof(message), "%s@%s set topic on %s: %s",
143 client_entry->nickname, client_entry->server,
144 channel_entry->channel_name, tmp);
146 snprintf(message, sizeof(message), "%s set topic on %s: %s",
147 client_entry->nickname, channel_entry->channel_name, tmp);
150 case SILC_NOTIFY_TYPE_NICK_CHANGE:
151 client_entry = va_arg(vp, SilcClientEntry);
152 client_entry2 = va_arg(vp, SilcClientEntry);
153 if (client_entry->server && client_entry2->server)
154 snprintf(message, sizeof(message), "%s@%s is known as %s@%s",
155 client_entry->nickname, client_entry->server,
156 client_entry2->nickname, client_entry2->server);
158 snprintf(message, sizeof(message), "%s is known as %s",
159 client_entry->nickname, client_entry2->nickname);
162 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
163 client_entry = va_arg(vp, SilcClientEntry);
164 tmp = silc_client_chmode(va_arg(vp, unsigned int));
165 channel_entry = va_arg(vp, SilcChannelEntry);
167 snprintf(message, sizeof(message), "%s changed channel mode to +%s",
168 client_entry->nickname, tmp);
170 snprintf(message, sizeof(message), "%s removed all channel modes",
171 client_entry->nickname);
172 if (app->screen->bottom_line->channel_mode)
173 silc_free(app->screen->bottom_line->channel_mode);
174 app->screen->bottom_line->channel_mode = tmp;
175 silc_screen_print_bottom_line(app->screen, 0);
178 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
179 client_entry = va_arg(vp, SilcClientEntry);
180 tmp_int = va_arg(vp, unsigned int);
181 tmp = silc_client_chumode(tmp_int);
182 client_entry2 = va_arg(vp, SilcClientEntry);
183 channel_entry = va_arg(vp, SilcChannelEntry);
185 snprintf(message, sizeof(message), "%s changed %s mode to +%s",
186 client_entry->nickname, client_entry2->nickname, tmp);
188 snprintf(message, sizeof(message), "%s removed %s modes",
189 client_entry->nickname, client_entry2->nickname);
190 if (client_entry2 == conn->local_entry) {
191 if (app->screen->bottom_line->mode)
192 silc_free(app->screen->bottom_line->mode);
193 app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
194 silc_screen_print_bottom_line(app->screen, 0);
199 case SILC_NOTIFY_TYPE_MOTD:
203 tmp = va_arg(vp, unsigned char *);
207 if (tmp[i++] == '\n') {
208 memset(line, 0, sizeof(line));
209 strncat(line, tmp, i - 1);
212 silc_say(client, conn, "%s", line);
222 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
229 silc_print(client, "*** %s", message);
232 /* Command handler. This function is called always in the command function.
233 If error occurs it will be called as well. `conn' is the associated
234 client connection. `cmd_context' is the command context that was
235 originally sent to the command. `success' is FALSE if error occured
236 during command. `command' is the command being processed. It must be
237 noted that this is not reply from server. This is merely called just
238 after application has called the command. Just to tell application
239 that the command really was processed. */
241 void silc_command(SilcClient client, SilcClientConnection conn,
242 SilcClientCommandContext cmd_context, int success,
245 SilcClientInternal app = (SilcClientInternal)client->application;
252 case SILC_COMMAND_QUIT:
253 app->screen->bottom_line->channel = NULL;
254 silc_screen_print_bottom_line(app->screen, 0);
257 case SILC_COMMAND_LEAVE:
259 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
260 app->screen->bottom_line->channel = NULL;
261 silc_screen_print_bottom_line(app->screen, 0);
269 /* Command reply handler. This function is called always in the command reply
270 function. If error occurs it will be called as well. Normal scenario
271 is that it will be called after the received command data has been parsed
272 and processed. The function is used to pass the received command data to
275 `conn' is the associated client connection. `cmd_payload' is the command
276 payload data received from server and it can be ignored. It is provided
277 if the application would like to re-parse the received command data,
278 however, it must be noted that the data is parsed already by the library
279 thus the payload can be ignored. `success' is FALSE if error occured.
280 In this case arguments are not sent to the application. `command' is the
281 command reply being processed. The function has variable argument list
282 and each command defines the number and type of arguments it passes to the
283 application (on error they are not sent). */
285 void silc_command_reply(SilcClient client, SilcClientConnection conn,
286 SilcCommandPayload cmd_payload, int success,
287 SilcCommand command, SilcCommandStatus status, ...)
289 SilcClientInternal app = (SilcClientInternal)client->application;
296 va_start(vp, status);
301 case SILC_COMMAND_JOIN:
305 app->screen->bottom_line->channel = va_arg(vp, char *);
306 (void)va_arg(vp, void *);
307 mode = va_arg(vp, unsigned int);
308 app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
309 silc_screen_print_bottom_line(app->screen, 0);
313 case SILC_COMMAND_NICK:
315 SilcClientEntry entry;
317 entry = va_arg(vp, SilcClientEntry);
318 silc_say(client, conn, "Your current nickname is %s", entry->nickname);
319 app->screen->bottom_line->nickname = entry->nickname;
320 silc_screen_print_bottom_line(app->screen, 0);
324 case SILC_COMMAND_USERS:
325 silc_list_start(conn->current_channel->clients);
326 while ((chu = silc_list_get(conn->current_channel->clients))
328 if (chu->client == conn->local_entry) {
329 if (app->screen->bottom_line->mode)
330 silc_free(app->screen->bottom_line->mode);
331 app->screen->bottom_line->mode =
332 silc_client_chumode_char(chu->mode);
333 silc_screen_print_bottom_line(app->screen, 0);
341 /* Called to indicate that connection was either successfully established
342 or connecting failed. This is also the first time application receives
343 the SilcClientConnection objecet which it should save somewhere. */
345 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
347 SilcClientInternal app = (SilcClientInternal)client->application;
350 app->screen->bottom_line->connection = conn->remote_host;
351 silc_screen_print_bottom_line(app->screen, 0);
356 /* Called to indicate that connection was disconnected to the server. */
358 void silc_disconnect(SilcClient client, SilcClientConnection conn)
360 SilcClientInternal app = (SilcClientInternal)client->application;
362 app->screen->bottom_line->connection = NULL;
363 silc_screen_print_bottom_line(app->screen, 0);
367 /* Asks passphrase from user on the input line. */
369 unsigned char *silc_ask_passphrase(SilcClient client,
370 SilcClientConnection conn)
372 SilcClientInternal app = (SilcClientInternal)conn->client->application;
373 char pass1[256], pass2[256];
380 wattroff(app->screen->input_win, A_INVIS);
381 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
382 wattron(app->screen->input_win, A_INVIS);
385 memset(pass1, 0, sizeof(pass1));
386 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
388 /* Print retype prompt */
389 wattroff(app->screen->input_win, A_INVIS);
390 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
391 wattron(app->screen->input_win, A_INVIS);
394 memset(pass2, 0, sizeof(pass2));
395 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
397 if (!strncmp(pass1, pass2, strlen(pass2)))
403 ret = silc_calloc(strlen(pass1), sizeof(char));
404 memcpy(ret, pass1, strlen(pass1));
406 memset(pass1, 0, sizeof(pass1));
407 memset(pass2, 0, sizeof(pass2));
409 wattroff(app->screen->input_win, A_INVIS);
410 silc_screen_input_reset(app->screen);
415 /* Verifies received public key. If user decides to trust the key it is
416 saved as trusted server key for later use. If user does not trust the
417 key this returns FALSE. */
419 int silc_verify_server_key(SilcClient client,
420 SilcClientConnection conn,
421 unsigned char *pk, unsigned int pk_len,
422 SilcSKEPKType pk_type)
424 SilcSocketConnection sock = conn->sock;
427 char *hostname, *fingerprint;
431 hostname = sock->hostname ? sock->hostname : sock->ip;
433 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
434 silc_say(client, conn, "We don't support server %s key type", hostname);
438 pw = getpwuid(getuid());
442 memset(filename, 0, sizeof(filename));
443 memset(file, 0, sizeof(file));
444 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
446 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
449 /* Check wheter this key already exists */
450 if (stat(filename, &st) < 0) {
452 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
453 silc_say(client, conn, "Received server %s public key", hostname);
454 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
455 silc_say(client, conn, "%s", fingerprint);
456 silc_free(fingerprint);
458 /* Ask user to verify the key and save it */
459 if (silc_client_ask_yes_no(client,
460 "Would you like to accept the key (y/n)? "))
462 /* Save the key for future checking */
463 silc_pkcs_save_public_key_data(filename, pk, pk_len,
468 /* The key already exists, verify it. */
469 SilcPublicKey public_key;
470 unsigned char *encpk;
471 unsigned int encpk_len;
473 /* Load the key file */
474 if (!silc_pkcs_load_public_key(filename, &public_key,
476 if (!silc_pkcs_load_public_key(filename, &public_key,
477 SILC_PKCS_FILE_BIN)) {
478 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
479 silc_say(client, conn, "Received server %s public key", hostname);
480 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
481 silc_say(client, conn, "%s", fingerprint);
482 silc_free(fingerprint);
483 silc_say(client, conn, "Could not load your local copy of the server %s key",
485 if (silc_client_ask_yes_no(client,
486 "Would you like to accept the key anyway (y/n)? "))
488 /* Save the key for future checking */
490 silc_pkcs_save_public_key_data(filename, pk, pk_len,
498 /* Encode the key data */
499 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
501 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
502 silc_say(client, conn, "Received server %s public key", hostname);
503 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
504 silc_say(client, conn, "%s", fingerprint);
505 silc_free(fingerprint);
506 silc_say(client, conn, "Your local copy of the server %s key is malformed",
508 if (silc_client_ask_yes_no(client,
509 "Would you like to accept the key anyway (y/n)? "))
511 /* Save the key for future checking */
513 silc_pkcs_save_public_key_data(filename, pk, pk_len,
521 if (memcmp(encpk, pk, encpk_len)) {
522 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
523 silc_say(client, conn, "Received server %s public key", hostname);
524 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
525 silc_say(client, conn, "%s", fingerprint);
526 silc_free(fingerprint);
527 silc_say(client, conn, "Server %s key does not match with your local copy",
529 silc_say(client, conn, "It is possible that the key has expired or changed");
530 silc_say(client, conn, "It is also possible that some one is performing "
531 "man-in-the-middle attack");
533 /* Ask user to verify the key and save it */
534 if (silc_client_ask_yes_no(client,
535 "Would you like to accept the key anyway (y/n)? "))
537 /* Save the key for future checking */
539 silc_pkcs_save_public_key_data(filename, pk, pk_len,
544 silc_say(client, conn, "Will not accept server %s key", hostname);
548 /* Local copy matched */
552 silc_say(client, conn, "Will not accept server %s key", hostname);
556 /* Find authentication method and authentication data by hostname and
557 port. The hostname may be IP address as well. The found authentication
558 method and authentication data is returned to `auth_meth', `auth_data'
559 and `auth_data_len'. The function returns TRUE if authentication method
560 is found and FALSE if not. `conn' may be NULL. */
562 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
563 char *hostname, unsigned short port,
564 SilcProtocolAuthMeth *auth_meth,
565 unsigned char **auth_data,
566 unsigned int *auth_data_len)
568 SilcClientInternal app = (SilcClientInternal)client->application;
570 if (app->config->conns) {
571 SilcClientConfigSectionConnection *conn = NULL;
573 /* Check if we find a match from user configured connections */
574 conn = silc_client_config_find_connection(app->config,
578 /* Match found. Use the configured authentication method */
579 *auth_meth = conn->auth_meth;
581 if (conn->auth_data) {
582 *auth_data = strdup(conn->auth_data);
583 *auth_data_len = strlen(conn->auth_data);
593 /* Notifies application that failure packet was received. This is called
594 if there is some protocol active in the client. The `protocol' is the
595 protocol context. The `failure' is opaque pointer to the failure
596 indication. Note, that the `failure' is protocol dependant and application
597 must explicitly cast it to correct type. Usually `failure' is 32 bit
598 failure type (see protocol specs for all protocol failure types). */
600 void silc_failure(SilcClient client, SilcClientConnection conn,
601 SilcProtocol protocol, void *failure)
603 SilcClientInternal app = (SilcClientInternal)client->application;
607 /* SILC client operations */
608 SilcClientOperations ops = {
610 channel_message: silc_channel_message,
611 private_message: silc_private_message,
613 command: silc_command,
614 command_reply: silc_command_reply,
615 connect: silc_connect,
616 disconnect: silc_disconnect,
617 get_auth_method: silc_get_auth_method,
618 verify_server_key: silc_verify_server_key,
619 ask_passphrase: silc_ask_passphrase,
620 failure: silc_failure,