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 if (client_entry->server)
134 snprintf(message, sizeof(message), "Signoff: %s@%s",
135 client_entry->nickname, client_entry->server);
137 snprintf(message, sizeof(message), "Signoff: %s",
138 client_entry->nickname);
141 case SILC_NOTIFY_TYPE_TOPIC_SET:
142 client_entry = va_arg(vp, SilcClientEntry);
143 tmp = va_arg(vp, char *);
144 channel_entry = va_arg(vp, SilcChannelEntry);
145 if (client_entry->server)
146 snprintf(message, sizeof(message), "%s@%s set topic on %s: %s",
147 client_entry->nickname, client_entry->server,
148 channel_entry->channel_name, tmp);
150 snprintf(message, sizeof(message), "%s set topic on %s: %s",
151 client_entry->nickname, channel_entry->channel_name, tmp);
154 case SILC_NOTIFY_TYPE_NICK_CHANGE:
155 client_entry = va_arg(vp, SilcClientEntry);
156 client_entry2 = va_arg(vp, SilcClientEntry);
157 if (client_entry->server && client_entry2->server)
158 snprintf(message, sizeof(message), "%s@%s is known as %s@%s",
159 client_entry->nickname, client_entry->server,
160 client_entry2->nickname, client_entry2->server);
162 snprintf(message, sizeof(message), "%s is known as %s",
163 client_entry->nickname, client_entry2->nickname);
166 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
167 client_entry = va_arg(vp, SilcClientEntry);
168 tmp = silc_client_chmode(va_arg(vp, unsigned int));
169 channel_entry = va_arg(vp, SilcChannelEntry);
171 snprintf(message, sizeof(message), "%s changed channel mode to +%s",
172 client_entry->nickname, tmp);
174 snprintf(message, sizeof(message), "%s removed all channel modes",
175 client_entry->nickname);
176 if (app->screen->bottom_line->channel_mode)
177 silc_free(app->screen->bottom_line->channel_mode);
178 app->screen->bottom_line->channel_mode = tmp;
179 silc_screen_print_bottom_line(app->screen, 0);
182 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
183 client_entry = va_arg(vp, SilcClientEntry);
184 tmp_int = va_arg(vp, unsigned int);
185 tmp = silc_client_chumode(tmp_int);
186 client_entry2 = va_arg(vp, SilcClientEntry);
187 channel_entry = va_arg(vp, SilcChannelEntry);
189 snprintf(message, sizeof(message), "%s changed %s's mode to +%s",
190 client_entry->nickname, client_entry2->nickname, tmp);
192 snprintf(message, sizeof(message), "%s removed %s's modes",
193 client_entry->nickname, client_entry2->nickname);
194 if (client_entry2 == conn->local_entry) {
195 if (app->screen->bottom_line->mode)
196 silc_free(app->screen->bottom_line->mode);
197 app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
198 silc_screen_print_bottom_line(app->screen, 0);
203 case SILC_NOTIFY_TYPE_MOTD:
207 tmp = va_arg(vp, unsigned char *);
211 if (tmp[i++] == '\n') {
212 memset(line, 0, sizeof(line));
213 strncat(line, tmp, i - 1);
216 silc_say(client, conn, "%s", line);
226 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
233 silc_print(client, "*** %s", message);
236 /* Command handler. This function is called always in the command function.
237 If error occurs it will be called as well. `conn' is the associated
238 client connection. `cmd_context' is the command context that was
239 originally sent to the command. `success' is FALSE if error occured
240 during command. `command' is the command being processed. It must be
241 noted that this is not reply from server. This is merely called just
242 after application has called the command. Just to tell application
243 that the command really was processed. */
245 void silc_command(SilcClient client, SilcClientConnection conn,
246 SilcClientCommandContext cmd_context, int success,
249 SilcClientInternal app = (SilcClientInternal)client->application;
256 case SILC_COMMAND_QUIT:
257 app->screen->bottom_line->channel = NULL;
258 silc_screen_print_bottom_line(app->screen, 0);
261 case SILC_COMMAND_LEAVE:
263 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
264 app->screen->bottom_line->channel = NULL;
265 silc_screen_print_bottom_line(app->screen, 0);
273 /* Command reply handler. This function is called always in the command reply
274 function. If error occurs it will be called as well. Normal scenario
275 is that it will be called after the received command data has been parsed
276 and processed. The function is used to pass the received command data to
279 `conn' is the associated client connection. `cmd_payload' is the command
280 payload data received from server and it can be ignored. It is provided
281 if the application would like to re-parse the received command data,
282 however, it must be noted that the data is parsed already by the library
283 thus the payload can be ignored. `success' is FALSE if error occured.
284 In this case arguments are not sent to the application. `command' is the
285 command reply being processed. The function has variable argument list
286 and each command defines the number and type of arguments it passes to the
287 application (on error they are not sent). */
289 void silc_command_reply(SilcClient client, SilcClientConnection conn,
290 SilcCommandPayload cmd_payload, int success,
291 SilcCommand command, SilcCommandStatus status, ...)
293 SilcClientInternal app = (SilcClientInternal)client->application;
300 va_start(vp, status);
305 case SILC_COMMAND_JOIN:
309 app->screen->bottom_line->channel = va_arg(vp, char *);
310 (void)va_arg(vp, void *);
311 mode = va_arg(vp, unsigned int);
312 app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
313 silc_screen_print_bottom_line(app->screen, 0);
317 case SILC_COMMAND_NICK:
319 SilcClientEntry entry;
321 entry = va_arg(vp, SilcClientEntry);
322 silc_say(client, conn, "Your current nickname is %s", entry->nickname);
323 app->screen->bottom_line->nickname = entry->nickname;
324 silc_screen_print_bottom_line(app->screen, 0);
328 case SILC_COMMAND_USERS:
329 silc_list_start(conn->current_channel->clients);
330 while ((chu = silc_list_get(conn->current_channel->clients))
332 if (chu->client == conn->local_entry) {
333 if (app->screen->bottom_line->mode)
334 silc_free(app->screen->bottom_line->mode);
335 app->screen->bottom_line->mode =
336 silc_client_chumode_char(chu->mode);
337 silc_screen_print_bottom_line(app->screen, 0);
345 /* Called to indicate that connection was either successfully established
346 or connecting failed. This is also the first time application receives
347 the SilcClientConnection objecet which it should save somewhere. */
349 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
351 SilcClientInternal app = (SilcClientInternal)client->application;
354 app->screen->bottom_line->connection = conn->remote_host;
355 silc_screen_print_bottom_line(app->screen, 0);
360 /* Called to indicate that connection was disconnected to the server. */
362 void silc_disconnect(SilcClient client, SilcClientConnection conn)
364 SilcClientInternal app = (SilcClientInternal)client->application;
366 app->screen->bottom_line->connection = NULL;
367 silc_screen_print_bottom_line(app->screen, 0);
371 /* Asks passphrase from user on the input line. */
373 unsigned char *silc_ask_passphrase(SilcClient client,
374 SilcClientConnection conn)
376 SilcClientInternal app = (SilcClientInternal)conn->client->application;
377 char pass1[256], pass2[256];
384 wattroff(app->screen->input_win, A_INVIS);
385 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
386 wattron(app->screen->input_win, A_INVIS);
389 memset(pass1, 0, sizeof(pass1));
390 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
392 /* Print retype prompt */
393 wattroff(app->screen->input_win, A_INVIS);
394 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
395 wattron(app->screen->input_win, A_INVIS);
398 memset(pass2, 0, sizeof(pass2));
399 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
401 if (!strncmp(pass1, pass2, strlen(pass2)))
407 ret = silc_calloc(strlen(pass1), sizeof(char));
408 memcpy(ret, pass1, strlen(pass1));
410 memset(pass1, 0, sizeof(pass1));
411 memset(pass2, 0, sizeof(pass2));
413 wattroff(app->screen->input_win, A_INVIS);
414 silc_screen_input_reset(app->screen);
419 /* Verifies received public key. If user decides to trust the key it is
420 saved as trusted server key for later use. If user does not trust the
421 key this returns FALSE. */
423 int silc_verify_server_key(SilcClient client,
424 SilcClientConnection conn,
425 unsigned char *pk, unsigned int pk_len,
426 SilcSKEPKType pk_type)
428 SilcSocketConnection sock = conn->sock;
431 char *hostname, *fingerprint;
435 hostname = sock->hostname ? sock->hostname : sock->ip;
437 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
438 silc_say(client, conn, "We don't support server %s key type", hostname);
442 pw = getpwuid(getuid());
446 memset(filename, 0, sizeof(filename));
447 memset(file, 0, sizeof(file));
448 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
450 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
453 /* Check wheter this key already exists */
454 if (stat(filename, &st) < 0) {
456 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
457 silc_say(client, conn, "Received server %s public key", hostname);
458 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
459 silc_say(client, conn, "%s", fingerprint);
460 silc_free(fingerprint);
462 /* Ask user to verify the key and save it */
463 if (silc_client_ask_yes_no(client,
464 "Would you like to accept the key (y/n)? "))
466 /* Save the key for future checking */
467 silc_pkcs_save_public_key_data(filename, pk, pk_len,
472 /* The key already exists, verify it. */
473 SilcPublicKey public_key;
474 unsigned char *encpk;
475 unsigned int encpk_len;
477 /* Load the key file */
478 if (!silc_pkcs_load_public_key(filename, &public_key,
480 if (!silc_pkcs_load_public_key(filename, &public_key,
481 SILC_PKCS_FILE_BIN)) {
482 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
483 silc_say(client, conn, "Received server %s public key", hostname);
484 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
485 silc_say(client, conn, "%s", fingerprint);
486 silc_free(fingerprint);
487 silc_say(client, conn, "Could not load your local copy of the server %s key",
489 if (silc_client_ask_yes_no(client,
490 "Would you like to accept the key anyway (y/n)? "))
492 /* Save the key for future checking */
494 silc_pkcs_save_public_key_data(filename, pk, pk_len,
502 /* Encode the key data */
503 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
505 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
506 silc_say(client, conn, "Received server %s public key", hostname);
507 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
508 silc_say(client, conn, "%s", fingerprint);
509 silc_free(fingerprint);
510 silc_say(client, conn, "Your local copy of the server %s key is malformed",
512 if (silc_client_ask_yes_no(client,
513 "Would you like to accept the key anyway (y/n)? "))
515 /* Save the key for future checking */
517 silc_pkcs_save_public_key_data(filename, pk, pk_len,
525 if (memcmp(encpk, pk, encpk_len)) {
526 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
527 silc_say(client, conn, "Received server %s public key", hostname);
528 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
529 silc_say(client, conn, "%s", fingerprint);
530 silc_free(fingerprint);
531 silc_say(client, conn, "Server %s key does not match with your local copy",
533 silc_say(client, conn, "It is possible that the key has expired or changed");
534 silc_say(client, conn, "It is also possible that some one is performing "
535 "man-in-the-middle attack");
537 /* Ask user to verify the key and save it */
538 if (silc_client_ask_yes_no(client,
539 "Would you like to accept the key anyway (y/n)? "))
541 /* Save the key for future checking */
543 silc_pkcs_save_public_key_data(filename, pk, pk_len,
548 silc_say(client, conn, "Will not accept server %s key", hostname);
552 /* Local copy matched */
556 silc_say(client, conn, "Will not accept server %s key", hostname);
560 /* Find authentication method and authentication data by hostname and
561 port. The hostname may be IP address as well. The found authentication
562 method and authentication data is returned to `auth_meth', `auth_data'
563 and `auth_data_len'. The function returns TRUE if authentication method
564 is found and FALSE if not. `conn' may be NULL. */
566 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
567 char *hostname, unsigned short port,
568 SilcProtocolAuthMeth *auth_meth,
569 unsigned char **auth_data,
570 unsigned int *auth_data_len)
572 SilcClientInternal app = (SilcClientInternal)client->application;
574 if (app->config->conns) {
575 SilcClientConfigSectionConnection *conn = NULL;
577 /* Check if we find a match from user configured connections */
578 conn = silc_client_config_find_connection(app->config,
582 /* Match found. Use the configured authentication method */
583 *auth_meth = conn->auth_meth;
585 if (conn->auth_data) {
586 *auth_data = strdup(conn->auth_data);
587 *auth_data_len = strlen(conn->auth_data);
597 /* Notifies application that failure packet was received. This is called
598 if there is some protocol active in the client. The `protocol' is the
599 protocol context. The `failure' is opaque pointer to the failure
600 indication. Note, that the `failure' is protocol dependant and application
601 must explicitly cast it to correct type. Usually `failure' is 32 bit
602 failure type (see protocol specs for all protocol failure types). */
604 void silc_failure(SilcClient client, SilcClientConnection conn,
605 SilcProtocol protocol, void *failure)
607 SilcClientInternal app = (SilcClientInternal)client->application;
611 /* SILC client operations */
612 SilcClientOperations ops = {
614 channel_message: silc_channel_message,
615 private_message: silc_private_message,
617 command: silc_command,
618 command_reply: silc_command_reply,
619 connect: silc_connect,
620 disconnect: silc_disconnect,
621 get_auth_method: silc_get_auth_method,
622 verify_server_key: silc_verify_server_key,
623 ask_passphrase: silc_ask_passphrase,
624 failure: silc_failure,