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:
236 silc_print(client, "*** %s", message);
239 /* Command handler. This function is called always in the command function.
240 If error occurs it will be called as well. `conn' is the associated
241 client connection. `cmd_context' is the command context that was
242 originally sent to the command. `success' is FALSE if error occured
243 during command. `command' is the command being processed. It must be
244 noted that this is not reply from server. This is merely called just
245 after application has called the command. Just to tell application
246 that the command really was processed. */
248 void silc_command(SilcClient client, SilcClientConnection conn,
249 SilcClientCommandContext cmd_context, int success,
252 SilcClientInternal app = (SilcClientInternal)client->application;
259 case SILC_COMMAND_QUIT:
260 app->screen->bottom_line->channel = NULL;
261 silc_screen_print_bottom_line(app->screen, 0);
264 case SILC_COMMAND_LEAVE:
266 if (!strncmp(conn->current_channel->channel_name, name, strlen(name))) {
267 app->screen->bottom_line->channel = NULL;
268 silc_screen_print_bottom_line(app->screen, 0);
276 /* Command reply handler. This function is called always in the command reply
277 function. If error occurs it will be called as well. Normal scenario
278 is that it will be called after the received command data has been parsed
279 and processed. The function is used to pass the received command data to
282 `conn' is the associated client connection. `cmd_payload' is the command
283 payload data received from server and it can be ignored. It is provided
284 if the application would like to re-parse the received command data,
285 however, it must be noted that the data is parsed already by the library
286 thus the payload can be ignored. `success' is FALSE if error occured.
287 In this case arguments are not sent to the application. `command' is the
288 command reply being processed. The function has variable argument list
289 and each command defines the number and type of arguments it passes to the
290 application (on error they are not sent). */
292 void silc_command_reply(SilcClient client, SilcClientConnection conn,
293 SilcCommandPayload cmd_payload, int success,
294 SilcCommand command, SilcCommandStatus status, ...)
296 SilcClientInternal app = (SilcClientInternal)client->application;
303 va_start(vp, status);
308 case SILC_COMMAND_JOIN:
312 app->screen->bottom_line->channel = va_arg(vp, char *);
313 (void)va_arg(vp, void *);
314 mode = va_arg(vp, unsigned int);
315 app->screen->bottom_line->channel_mode = silc_client_chmode(mode);
316 silc_screen_print_bottom_line(app->screen, 0);
320 case SILC_COMMAND_NICK:
322 SilcClientEntry entry;
324 entry = va_arg(vp, SilcClientEntry);
325 silc_say(client, conn, "Your current nickname is %s", entry->nickname);
326 app->screen->bottom_line->nickname = entry->nickname;
327 silc_screen_print_bottom_line(app->screen, 0);
331 case SILC_COMMAND_USERS:
332 silc_list_start(conn->current_channel->clients);
333 while ((chu = silc_list_get(conn->current_channel->clients))
335 if (chu->client == conn->local_entry) {
336 if (app->screen->bottom_line->mode)
337 silc_free(app->screen->bottom_line->mode);
338 app->screen->bottom_line->mode =
339 silc_client_chumode_char(chu->mode);
340 silc_screen_print_bottom_line(app->screen, 0);
348 /* Called to indicate that connection was either successfully established
349 or connecting failed. This is also the first time application receives
350 the SilcClientConnection objecet which it should save somewhere. */
352 void silc_connect(SilcClient client, SilcClientConnection conn, int success)
354 SilcClientInternal app = (SilcClientInternal)client->application;
357 app->screen->bottom_line->connection = conn->remote_host;
358 silc_screen_print_bottom_line(app->screen, 0);
363 /* Called to indicate that connection was disconnected to the server. */
365 void silc_disconnect(SilcClient client, SilcClientConnection conn)
367 SilcClientInternal app = (SilcClientInternal)client->application;
369 app->screen->bottom_line->connection = NULL;
370 silc_screen_print_bottom_line(app->screen, 0);
374 /* Asks passphrase from user on the input line. */
376 unsigned char *silc_ask_passphrase(SilcClient client,
377 SilcClientConnection conn)
379 SilcClientInternal app = (SilcClientInternal)conn->client->application;
380 char pass1[256], pass2[256];
387 wattroff(app->screen->input_win, A_INVIS);
388 silc_screen_input_print_prompt(app->screen, "Passphrase: ");
389 wattron(app->screen->input_win, A_INVIS);
392 memset(pass1, 0, sizeof(pass1));
393 wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
395 /* Print retype prompt */
396 wattroff(app->screen->input_win, A_INVIS);
397 silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
398 wattron(app->screen->input_win, A_INVIS);
401 memset(pass2, 0, sizeof(pass2));
402 wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
404 if (!strncmp(pass1, pass2, strlen(pass2)))
410 ret = silc_calloc(strlen(pass1), sizeof(char));
411 memcpy(ret, pass1, strlen(pass1));
413 memset(pass1, 0, sizeof(pass1));
414 memset(pass2, 0, sizeof(pass2));
416 wattroff(app->screen->input_win, A_INVIS);
417 silc_screen_input_reset(app->screen);
422 /* Verifies received public key. If user decides to trust the key it is
423 saved as trusted server key for later use. If user does not trust the
424 key this returns FALSE. */
426 int silc_verify_server_key(SilcClient client,
427 SilcClientConnection conn,
428 unsigned char *pk, unsigned int pk_len,
429 SilcSKEPKType pk_type)
431 SilcSocketConnection sock = conn->sock;
434 char *hostname, *fingerprint;
438 hostname = sock->hostname ? sock->hostname : sock->ip;
440 if (pk_type != SILC_SKE_PK_TYPE_SILC) {
441 silc_say(client, conn, "We don't support server %s key type", hostname);
445 pw = getpwuid(getuid());
449 memset(filename, 0, sizeof(filename));
450 memset(file, 0, sizeof(file));
451 snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", hostname,
453 snprintf(filename, sizeof(filename) - 1, "%s/.silc/serverkeys/%s",
456 /* Check wheter this key already exists */
457 if (stat(filename, &st) < 0) {
459 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
460 silc_say(client, conn, "Received server %s public key", hostname);
461 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
462 silc_say(client, conn, "%s", fingerprint);
463 silc_free(fingerprint);
465 /* Ask user to verify the key and save it */
466 if (silc_client_ask_yes_no(client,
467 "Would you like to accept the key (y/n)? "))
469 /* Save the key for future checking */
470 silc_pkcs_save_public_key_data(filename, pk, pk_len,
475 /* The key already exists, verify it. */
476 SilcPublicKey public_key;
477 unsigned char *encpk;
478 unsigned int encpk_len;
480 /* Load the key file */
481 if (!silc_pkcs_load_public_key(filename, &public_key,
483 if (!silc_pkcs_load_public_key(filename, &public_key,
484 SILC_PKCS_FILE_BIN)) {
485 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
486 silc_say(client, conn, "Received server %s public key", hostname);
487 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
488 silc_say(client, conn, "%s", fingerprint);
489 silc_free(fingerprint);
490 silc_say(client, conn, "Could not load your local copy of the server %s key",
492 if (silc_client_ask_yes_no(client,
493 "Would you like to accept the key anyway (y/n)? "))
495 /* Save the key for future checking */
497 silc_pkcs_save_public_key_data(filename, pk, pk_len,
505 /* Encode the key data */
506 encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
508 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
509 silc_say(client, conn, "Received server %s public key", hostname);
510 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
511 silc_say(client, conn, "%s", fingerprint);
512 silc_free(fingerprint);
513 silc_say(client, conn, "Your local copy of the server %s key is malformed",
515 if (silc_client_ask_yes_no(client,
516 "Would you like to accept the key anyway (y/n)? "))
518 /* Save the key for future checking */
520 silc_pkcs_save_public_key_data(filename, pk, pk_len,
528 if (memcmp(encpk, pk, encpk_len)) {
529 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
530 silc_say(client, conn, "Received server %s public key", hostname);
531 silc_say(client, conn, "Fingerprint for the server %s key is", hostname);
532 silc_say(client, conn, "%s", fingerprint);
533 silc_free(fingerprint);
534 silc_say(client, conn, "Server %s key does not match with your local copy",
536 silc_say(client, conn, "It is possible that the key has expired or changed");
537 silc_say(client, conn, "It is also possible that some one is performing "
538 "man-in-the-middle attack");
540 /* Ask user to verify the key and save it */
541 if (silc_client_ask_yes_no(client,
542 "Would you like to accept the key anyway (y/n)? "))
544 /* Save the key for future checking */
546 silc_pkcs_save_public_key_data(filename, pk, pk_len,
551 silc_say(client, conn, "Will not accept server %s key", hostname);
555 /* Local copy matched */
559 silc_say(client, conn, "Will not accept server %s key", hostname);
563 /* Find authentication method and authentication data by hostname and
564 port. The hostname may be IP address as well. The found authentication
565 method and authentication data is returned to `auth_meth', `auth_data'
566 and `auth_data_len'. The function returns TRUE if authentication method
567 is found and FALSE if not. `conn' may be NULL. */
569 int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
570 char *hostname, unsigned short port,
571 SilcProtocolAuthMeth *auth_meth,
572 unsigned char **auth_data,
573 unsigned int *auth_data_len)
575 SilcClientInternal app = (SilcClientInternal)client->application;
577 if (app->config->conns) {
578 SilcClientConfigSectionConnection *conn = NULL;
580 /* Check if we find a match from user configured connections */
581 conn = silc_client_config_find_connection(app->config,
585 /* Match found. Use the configured authentication method */
586 *auth_meth = conn->auth_meth;
588 if (conn->auth_data) {
589 *auth_data = strdup(conn->auth_data);
590 *auth_data_len = strlen(conn->auth_data);
600 /* Notifies application that failure packet was received. This is called
601 if there is some protocol active in the client. The `protocol' is the
602 protocol context. The `failure' is opaque pointer to the failure
603 indication. Note, that the `failure' is protocol dependant and application
604 must explicitly cast it to correct type. Usually `failure' is 32 bit
605 failure type (see protocol specs for all protocol failure types). */
607 void silc_failure(SilcClient client, SilcClientConnection conn,
608 SilcProtocol protocol, void *failure)
610 SilcClientInternal app = (SilcClientInternal)client->application;
614 /* SILC client operations */
615 SilcClientOperations ops = {
617 channel_message: silc_channel_message,
618 private_message: silc_private_message,
620 command: silc_command,
621 command_reply: silc_command_reply,
622 connect: silc_connect,
623 disconnect: silc_disconnect,
624 get_auth_method: silc_get_auth_method,
625 verify_server_key: silc_verify_server_key,
626 ask_passphrase: silc_ask_passphrase,
627 failure: silc_failure,