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 (!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_payload' is the Notify
69 Payload received from server. Client library may parse it to cache
70 some data received from the payload but it is the application's
71 responsiblity to retrieve the message and arguments from the payload.
72 The message in the payload sent by server is implementation specific
73 thus it is recommended that application will generate its own message. */
74 /* XXX should generate own messages based on notify type. */
76 void silc_notify(SilcClient client, SilcClientConnection conn,
77 SilcNotifyPayload notify_payload)
80 SilcArgumentPayload args;
84 type = silc_notify_get_type(notify_payload);
85 msg = silc_notify_get_message(notify_payload);
86 args = silc_notify_get_args(notify_payload);
88 memset(message, 0, sizeof(message));
90 /* Get arguments (defined by protocol in silc-pp-01 -draft) */
92 case SILC_NOTIFY_TYPE_NONE:
93 strncat(message, msg, strlen(msg));
95 case SILC_NOTIFY_TYPE_INVITE:
96 snprintf(message, sizeof(message), msg,
97 silc_argument_get_arg_type(args, 1, NULL),
98 silc_argument_get_arg_type(args, 2, NULL));
100 case SILC_NOTIFY_TYPE_JOIN:
101 snprintf(message, sizeof(message), msg,
102 silc_argument_get_arg_type(args, 2, NULL),
103 silc_argument_get_arg_type(args, 3, NULL),
104 silc_argument_get_arg_type(args, 4, NULL),
105 silc_argument_get_arg_type(args, 6, NULL));
107 case SILC_NOTIFY_TYPE_LEAVE:
108 snprintf(message, sizeof(message), msg,
109 silc_argument_get_arg_type(args, 1, NULL),
110 silc_argument_get_arg_type(args, 2, NULL),
111 silc_argument_get_arg_type(args, 4, NULL));
113 case SILC_NOTIFY_TYPE_SIGNOFF:
114 snprintf(message, sizeof(message), msg,
115 silc_argument_get_arg_type(args, 1, NULL),
116 silc_argument_get_arg_type(args, 2, NULL));
118 case SILC_NOTIFY_TYPE_TOPIC_SET:
119 snprintf(message, sizeof(message), msg,
120 silc_argument_get_arg_type(args, 3, NULL),
121 silc_argument_get_arg_type(args, 4, NULL),
122 silc_argument_get_arg_type(args, 2, NULL));
128 silc_print(client, "*** %s", message);
131 /* Command handler. This function is called always in the command function.
132 If error occurs it will be called as well. `conn' is the associated
133 client connection. `cmd_context' is the command context that was
134 originally sent to the command. `success' is FALSE if error occured
135 during command. `command' is the command being processed. It must be
136 noted that this is not reply from server. This is merely called just
137 after application has called the command. Just to tell application
138 that the command really was processed. */
140 void silc_command(SilcClient client, SilcClientConnection conn,
141 SilcClientCommandContext cmd_context, int success,
144 SilcClientInternal app = (SilcClientInternal)client->application;
151 case SILC_COMMAND_QUIT:
152 app->screen->bottom_line->channel = NULL;
153 silc_screen_print_bottom_line(app->screen, 0);
156 case SILC_COMMAND_LEAVE:
158 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
159 app->screen->bottom_line->channel = NULL;
160 silc_screen_print_bottom_line(app->screen, 0);
168 /* Command reply handler. This function is called always in the command reply
169 function. If error occurs it will be called as well. Normal scenario
170 is that it will be called after the received command data has been parsed
171 and processed. The function is used to pass the received command data to
174 `conn' is the associated client connection. `cmd_payload' is the command
175 payload data received from server and it can be ignored. It is provided
176 if the application would like to re-parse the received command data,
177 however, it must be noted that the data is parsed already by the library
178 thus the payload can be ignored. `success' is FALSE if error occured.
179 In this case arguments are not sent to the application. `command' is the
180 command reply being processed. The function has variable argument list
181 and each command defines the number and type of arguments it passes to the
182 application (on error they are not sent). */
184 void silc_command_reply(SilcClient client, SilcClientConnection conn,
185 SilcCommandPayload cmd_payload, int success,
186 SilcCommandStatus status, SilcCommand command, ...)
188 SilcClientInternal app = (SilcClientInternal)client->application;
194 va_start(vp, command);
199 case SILC_COMMAND_JOIN:
200 app->screen->bottom_line->channel = va_arg(vp, char *);
201 silc_screen_print_bottom_line(app->screen, 0);
204 case SILC_COMMAND_NICK:
205 app->screen->bottom_line->nickname = va_arg(vp, char *);
206 silc_screen_print_bottom_line(app->screen, 0);
212 /* Called to indicate that connection was either successfully established
213 or connecting failed. This is also the first time application receives
214 the SilcClientConnection objecet which it should save somewhere. */
216 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
218 SilcClientInternal app = (SilcClientInternal)client->application;
221 app->screen->bottom_line->connection = conn->remote_host;
222 silc_screen_print_bottom_line(app->screen, 0);
227 /* Called to indicate that connection was disconnected to the server. */
229 void silc_disconnect(SilcClient client, SilcClientConnection conn)
231 SilcClientInternal app = (SilcClientInternal)client->application;
233 app->screen->bottom_line->connection = NULL;
234 silc_screen_print_bottom_line(app->screen, 0);
237 /* Asks passphrase from user on the input line. */
239 unsigned char *silc_ask_passphrase(SilcClient client,
240 SilcClientConnection conn)
242 SilcClientInternal app = (SilcClientInternal)conn->client->application;
243 char pass1[256], pass2[256];
250 wattroff(app->screen->input_win, A_INVIS);
251 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
252 wattron(app->screen->input_win, A_INVIS);
255 memset(pass1, 0, sizeof(pass1));
256 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
258 /* Print retype prompt */
259 wattroff(app->screen->input_win, A_INVIS);
260 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
261 wattron(app->screen->input_win, A_INVIS);
264 memset(pass2, 0, sizeof(pass2));
265 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
267 if (!strncmp(pass1, pass2, strlen(pass2)))
273 ret = silc_calloc(strlen(pass1), sizeof(char));
274 memcpy(ret, pass1, strlen(pass1));
276 memset(pass1, 0, sizeof(pass1));
277 memset(pass2, 0, sizeof(pass2));
279 wattroff(app->screen->input_win, A_INVIS);
280 silc_screen_input_reset(app->screen);
285 /* Verifies received public key. If user decides to trust the key it is
286 saved as trusted server key for later use. If user does not trust the
287 key this returns FALSE. */
289 int silc_verify_server_key(SilcClient client,
290 SilcClientConnection conn,
291 unsigned char *pk, unsigned int pk_len,
292 SilcSKEPKType pk_type)
294 SilcSocketConnection sock = conn->sock;
297 char *hostname, *fingerprint;
301 hostname = sock->hostname ? sock->hostname : sock->ip;
303 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
304 silc_say(client, conn, "We don't support server %s key type", hostname);
308 pw = getpwuid(getuid());
312 memset(filename, 0, sizeof(filename));
313 memset(file, 0, sizeof(file));
314 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
316 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
319 /* Check wheter this key already exists */
320 if (stat(filename, &st) < 0) {
322 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
323 silc_say(client, conn, "Received server %s public key", hostname);
324 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
325 silc_say(client, conn, "%s", fingerprint);
326 silc_free(fingerprint);
328 /* Ask user to verify the key and save it */
329 if (silc_client_ask_yes_no(client,
330 "Would you like to accept the key (y/n)? "))
332 /* Save the key for future checking */
333 silc_pkcs_save_public_key_data(filename, pk, pk_len,
338 /* The key already exists, verify it. */
339 SilcPublicKey public_key;
340 unsigned char *encpk;
341 unsigned int encpk_len;
343 /* Load the key file */
344 if (!silc_pkcs_load_public_key(filename, &public_key,
346 if (!silc_pkcs_load_public_key(filename, &public_key,
347 SILC_PKCS_FILE_BIN)) {
348 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
349 silc_say(client, conn, "Received server %s public key", hostname);
350 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
351 silc_say(client, conn, "%s", fingerprint);
352 silc_free(fingerprint);
353 silc_say(client, conn, "Could not load your local copy of the server %s key",
355 if (silc_client_ask_yes_no(client,
356 "Would you like to accept the key anyway (y/n)? "))
358 /* Save the key for future checking */
360 silc_pkcs_save_public_key_data(filename, pk, pk_len,
368 /* Encode the key data */
369 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
371 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
372 silc_say(client, conn, "Received server %s public key", hostname);
373 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
374 silc_say(client, conn, "%s", fingerprint);
375 silc_free(fingerprint);
376 silc_say(client, conn, "Your local copy of the server %s key is malformed",
378 if (silc_client_ask_yes_no(client,
379 "Would you like to accept the key anyway (y/n)? "))
381 /* Save the key for future checking */
383 silc_pkcs_save_public_key_data(filename, pk, pk_len,
391 if (memcmp(encpk, pk, encpk_len)) {
392 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
393 silc_say(client, conn, "Received server %s public key", hostname);
394 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
395 silc_say(client, conn, "%s", fingerprint);
396 silc_free(fingerprint);
397 silc_say(client, conn, "Server %s key does not match with your local copy",
399 silc_say(client, conn, "It is possible that the key has expired or changed");
400 silc_say(client, conn, "It is also possible that some one is performing "
401 "man-in-the-middle attack");
403 /* Ask user to verify the key and save it */
404 if (silc_client_ask_yes_no(client,
405 "Would you like to accept the key anyway (y/n)? "))
407 /* Save the key for future checking */
409 silc_pkcs_save_public_key_data(filename, pk, pk_len,
414 silc_say(client, conn, "Will not accept server %s key", hostname);
418 /* Local copy matched */
422 silc_say(client, conn, "Will not accept server %s key", hostname);
426 /* Find authentication method and authentication data by hostname and
427 port. The hostname may be IP address as well. The found authentication
428 method and authentication data is returned to `auth_meth', `auth_data'
429 and `auth_data_len'. The function returns TRUE if authentication method
430 is found and FALSE if not. `conn' may be NULL. */
432 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
433 char *hostname, unsigned short port,
434 SilcProtocolAuthMeth *auth_meth,
435 unsigned char **auth_data,
436 unsigned int *auth_data_len)
438 SilcClientInternal app = (SilcClientInternal)client->application;
440 if (app->config->conns) {
441 SilcClientConfigSectionConnection *conn = NULL;
443 /* Check if we find a match from user configured connections */
444 conn = silc_client_config_find_connection(app->config,
448 /* Match found. Use the configured authentication method */
449 *auth_meth = conn->auth_meth;
451 if (conn->auth_data) {
452 *auth_data = strdup(conn->auth_data);
453 *auth_data_len = strlen(conn->auth_data);
463 /* SILC client operations */
464 SilcClientOperations ops = {
466 channel_message: silc_channel_message,
467 private_message: silc_private_message,
469 command: silc_command,
470 command_reply: silc_command_reply,
471 connect: silc_connect,
472 disconnect: silc_disconnect,
473 get_auth_method: silc_get_auth_method,
474 verify_server_key: silc_verify_server_key,
475 ask_passphrase: silc_ask_passphrase,