5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 2001 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.
22 #include "chat-protocols.h"
25 #include "servers-setup.h"
26 #include "channels-setup.h"
27 #include "silc-servers.h"
28 #include "silc-channels.h"
29 #include "silc-queries.h"
30 #include "silc-nicklist.h"
31 #include "version_internal.h"
36 #include "fe-common/core/printtext.h"
37 #include "fe-common/core/fe-channels.h"
39 #define SILC_CLIENT_PUBLIC_KEY_NAME "public_key.pub"
40 #define SILC_CLIENT_PRIVATE_KEY_NAME "private_key.prv"
42 #define SILC_CLIENT_DEF_PKCS "rsa"
43 #define SILC_CLIENT_DEF_PKCS_LEN 1024
45 SilcClient silc_client;
46 const char *silc_version_string = SILC_PROTOCOL_VERSION_STRING;
50 extern SilcClientOperations ops;
52 static void silc_say(SilcClient client, SilcClientConnection conn,
55 SILC_SERVER_REC *server;
59 server = conn == NULL ? NULL : conn->context;
62 str = g_strdup_vprintf(msg, va);
63 printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
68 /* Message for a channel. The `sender' is the nickname of the sender
69 received in the packet. The `channel_name' is the name of the channel. */
72 silc_channel_message(SilcClient client, SilcClientConnection conn,
73 SilcClientEntry sender, SilcChannelEntry channel,
74 SilcMessageFlags flags, char *msg)
76 SILC_SERVER_REC *server;
78 SILC_CHANNEL_REC *chanrec;
80 server = conn == NULL ? NULL : conn->context;
81 chanrec = silc_channel_find_entry(server, channel);
83 nick = silc_nicklist_find(chanrec, sender);
84 signal_emit("message public", 6, server, msg,
85 nick == NULL ? "(unknown)" : nick->nick,
86 nick == NULL ? NULL : nick->host,
90 /* Private message to the client. The `sender' is the nickname of the
91 sender received in the packet. */
94 silc_private_message(SilcClient client, SilcClientConnection conn,
95 SilcClientEntry sender, SilcMessageFlags flags,
98 SILC_SERVER_REC *server;
100 server = conn == NULL ? NULL : conn->context;
101 signal_emit("message private", 4, server, msg,
102 sender->nickname ? sender->nickname : "(unknown)",
103 sender->username ? sender->username : NULL);
106 /* Notify message to the client. The notify arguments are sent in the
107 same order as servers sends them. The arguments are same as received
108 from the server except for ID's. If ID is received application receives
109 the corresponding entry to the ID. For example, if Client ID is received
110 application receives SilcClientEntry. Also, if the notify type is
111 for channel the channel entry is sent to application (even if server
112 does not send it). */
119 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
120 static NOTIFY_REC notifies[] = {
121 { SILC_NOTIFY_TYPE_NONE, NULL },
122 { SILC_NOTIFY_TYPE_INVITE, "invite" },
123 { SILC_NOTIFY_TYPE_JOIN, "join" },
124 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
125 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
126 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
127 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
128 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
129 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
130 { SILC_NOTIFY_TYPE_MOTD, "motd" }
133 static void silc_notify(SilcClient client, SilcClientConnection conn,
134 SilcNotifyType type, ...)
136 SILC_SERVER_REC *server;
139 server = conn == NULL ? NULL : conn->context;
142 if (type == SILC_NOTIFY_TYPE_NONE) {
143 /* some generic notice from server */
144 printtext(server, NULL, MSGLEVEL_CRAP, "%s",
145 (char *) va_arg(va, char *));
146 } else if (type < MAX_NOTIFY) {
147 /* send signal about the notify event */
150 g_snprintf(signal, sizeof(signal), "silc event %s",
151 notifies[type].name);
152 signal_emit(signal, 2, server, va);
155 printtext(server, NULL, MSGLEVEL_CRAP,
156 "Unknown notify %d", type);
161 /* Called to indicate that connection was either successfully established
162 or connecting failed. This is also the first time application receives
163 the SilcClientConnection objecet which it should save somewhere. */
166 silc_connect(SilcClient client, SilcClientConnection conn, int success)
168 SILC_SERVER_REC *server = conn->context;
171 server->connected = TRUE;
172 signal_emit("event connected", 1, server);
174 server->connection_lost = TRUE;
175 server->conn->context = NULL;
176 server_disconnect(SERVER(server));
180 /* Called to indicate that connection was disconnected to the server. */
183 silc_disconnect(SilcClient client, SilcClientConnection conn)
185 SILC_SERVER_REC *server = conn->context;
187 server->conn->context = NULL;
189 server->connection_lost = TRUE;
190 server_disconnect(SERVER(server));
193 /* Command handler. This function is called always in the command function.
194 If error occurs it will be called as well. `conn' is the associated
195 client connection. `cmd_context' is the command context that was
196 originally sent to the command. `success' is FALSE if error occured
197 during command. `command' is the command being processed. It must be
198 noted that this is not reply from server. This is merely called just
199 after application has called the command. Just to tell application
200 that the command really was processed. */
203 silc_command(SilcClient client, SilcClientConnection conn,
204 SilcClientCommandContext cmd_context, int success,
209 /* Command reply handler. This function is called always in the command reply
210 function. If error occurs it will be called as well. Normal scenario
211 is that it will be called after the received command data has been parsed
212 and processed. The function is used to pass the received command data to
215 `conn' is the associated client connection. `cmd_payload' is the command
216 payload data received from server and it can be ignored. It is provided
217 if the application would like to re-parse the received command data,
218 however, it must be noted that the data is parsed already by the library
219 thus the payload can be ignored. `success' is FALSE if error occured.
220 In this case arguments are not sent to the application. `command' is the
221 command reply being processed. The function has variable argument list
222 and each command defines the number and type of arguments it passes to the
223 application (on error they are not sent). */
226 silc_command_reply(SilcClient client, SilcClientConnection conn,
227 SilcCommandPayload cmd_payload, int success,
228 SilcCommand command, SilcCommandStatus status, ...)
231 SILC_SERVER_REC *server = conn->context;
232 SILC_CHANNEL_REC *chanrec;
235 va_start(va, status);
237 /*g_snprintf(signal, sizeof(signal), "silc command reply %s",
238 silc_commands[type]);
239 signal_emit(signal, 2, server, va);*/
242 case SILC_COMMAND_JOIN:
244 char *channel, *mode;
246 SilcChannelEntry channel_entry;
248 channel = va_arg(va, char *);
249 channel_entry = va_arg(va, SilcChannelEntry);
250 modei = va_arg(va, uint32);
251 mode = silc_client_chmode(modei, channel_entry);
253 chanrec = silc_channel_find(server, channel);
254 if (chanrec != NULL && !success)
255 channel_destroy(CHANNEL(chanrec));
256 else if (chanrec == NULL && success)
257 chanrec = silc_channel_create(server, channel, TRUE);
259 g_free_not_null(chanrec->mode);
260 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
261 signal_emit("channel mode changed", 1, chanrec);
264 case SILC_COMMAND_NICK:
266 SilcClientEntry client = va_arg(va, SilcClientEntry);
269 old = g_strdup(server->nick);
270 server_change_nick(SERVER(server), client->nickname);
271 nicklist_rename_unique(SERVER(server),
272 server->conn->local_entry, server->nick,
273 client, client->nickname);
275 signal_emit("message own_nick", 4,
276 server, server->nick, old, "");
280 case SILC_COMMAND_USERS:
282 SilcChannelEntry channel;
283 SilcChannelUser user;
286 channel = va_arg(va, SilcChannelEntry);
287 chanrec = silc_channel_find_entry(server, channel);
291 silc_list_start(channel->clients);
292 while ((user = silc_list_get(channel->clients)) != NULL)
293 silc_nicklist_insert(chanrec, user, FALSE);
295 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
296 nicklist_set_own(CHANNEL(chanrec), ownnick);
297 signal_emit("channel joined", 1, chanrec);
298 fe_channels_nicklist(CHANNEL(chanrec),
299 CHANNEL_NICKLIST_FLAG_ALL);
307 /* Verifies received public key. If user decides to trust the key it is
308 saved as public server key for later use. If user does not trust the
309 key this returns FALSE. */
311 static int silc_verify_public_key(SilcClient client,
312 SilcClientConnection conn,
313 SilcSocketType conn_type,
314 unsigned char *pk, uint32 pk_len,
315 SilcSKEPKType pk_type)
320 /* Asks passphrase from user on the input line. */
322 static unsigned char *silc_ask_passphrase(SilcClient client,
323 SilcClientConnection conn)
328 /* Find authentication method and authentication data by hostname and
329 port. The hostname may be IP address as well. The found authentication
330 method and authentication data is returned to `auth_meth', `auth_data'
331 and `auth_data_len'. The function returns TRUE if authentication method
332 is found and FALSE if not. `conn' may be NULL. */
335 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
336 char *hostname, uint16 port,
337 SilcProtocolAuthMeth *auth_meth,
338 unsigned char **auth_data,
339 uint32 *auth_data_len)
344 /* Notifies application that failure packet was received. This is called
345 if there is some protocol active in the client. The `protocol' is the
346 protocol context. The `failure' is opaque pointer to the failure
347 indication. Note, that the `failure' is protocol dependant and application
348 must explicitly cast it to correct type. Usually `failure' is 32 bit
349 failure type (see protocol specs for all protocol failure types). */
352 silc_failure(SilcClient client, SilcClientConnection conn,
353 SilcProtocol protocol, void *failure)
355 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
356 SilcSKEStatus status = (SilcSKEStatus)failure;
358 if (status == SILC_SKE_STATUS_BAD_VERSION)
359 silc_say(client, conn,
360 "You are running incompatible client version (it may be "
361 "too old or too new)");
362 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
363 silc_say(client, conn, "Server does not support your public key type");
364 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
365 silc_say(client, conn,
366 "Server does not support one of your proposed KE group");
367 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
368 silc_say(client, conn,
369 "Server does not support one of your proposed cipher");
370 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
371 silc_say(client, conn,
372 "Server does not support one of your proposed PKCS");
373 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
374 silc_say(client, conn,
375 "Server does not support one of your proposed hash function");
376 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
377 silc_say(client, conn,
378 "Server does not support one of your proposed HMAC");
379 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
380 silc_say(client, conn, "Incorrect signature");
383 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
384 uint32 err = (uint32)failure;
386 if (err == SILC_AUTH_FAILED)
387 silc_say(client, conn, "Authentication failed");
391 /* Asks whether the user would like to perform the key agreement protocol.
392 This is called after we have received an key agreement packet or an
393 reply to our key agreement packet. This returns TRUE if the user wants
394 the library to perform the key agreement protocol and FALSE if it is not
395 desired (application may start it later by calling the function
396 silc_client_perform_key_agreement). */
399 silc_key_agreement(SilcClient client, SilcClientConnection conn,
400 SilcClientEntry client_entry, char *hostname,
402 SilcKeyAgreementCallback *completion,
407 /* We will just display the info on the screen and return FALSE and user
408 will have to start the key agreement with a command. */
411 memset(host, 0, sizeof(host));
412 snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
415 silc_say(client, conn, "%s wants to perform key agreement %s",
416 client_entry->nickname, hostname ? host : "");
424 /* SILC client operations */
425 SilcClientOperations ops = {
427 silc_channel_message,
428 silc_private_message,
434 silc_get_auth_method,
435 silc_verify_public_key,
441 /* Loads public and private key from files. */
443 static void silc_client_create_key_pair(char *pkcs_name, int bits,
445 SilcPublicKey *pub_key,
446 SilcPrivateKey *prv_key)
453 rng = silc_rng_alloc();
455 silc_rng_global_init(rng);
457 silc_pkcs_alloc(pkcs_name, &pkcs);
458 pkcs->pkcs->init(pkcs->context, bits, rng);
460 /* Create public key */
461 key = silc_pkcs_get_public_key(pkcs, &key_len);
462 *pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
465 memset(key, 0, sizeof(key_len));
468 /* Create private key */
469 key = silc_pkcs_get_private_key(pkcs, &key_len);
470 *prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
472 memset(key, 0, sizeof(key_len));
476 silc_pkcs_free(pkcs);
479 static int read_keyfiles(SilcClient client, char *public_file,
484 if (stat(public_file, &statbuf) != 0 ||
485 stat(private_file, &statbuf) != 0)
488 if (!silc_pkcs_load_private_key(private_file, &client->private_key,
489 SILC_PKCS_FILE_BIN) &&
490 !silc_pkcs_load_private_key(private_file, &client->private_key,
494 if (!silc_pkcs_load_public_key(public_file, &client->public_key,
495 SILC_PKCS_FILE_PEM) &&
496 !silc_pkcs_load_public_key(public_file, &client->public_key,
503 static char *silc_create_identifier(SilcClient client)
505 char hostname[256], *email, *ret;
507 if (gethostname(hostname, sizeof(hostname)) != 0)
510 email = g_strdup_printf("%s@%s", client->username, hostname);
511 ret = silc_pkcs_encode_identifier(client->username, hostname,
512 client->realname, email,
518 static int load_keys(SilcClient client)
520 char *public_file, *private_file;
523 public_file = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(),
524 SILC_CLIENT_PUBLIC_KEY_NAME);
525 private_file = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(),
526 SILC_CLIENT_PRIVATE_KEY_NAME);
528 if (!read_keyfiles(client, public_file, private_file)) {
529 /* couldn't read key files, recreate them */
530 identifier = silc_create_identifier(client);
531 silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS,
532 SILC_CLIENT_DEF_PKCS_LEN,
535 &client->private_key);
536 silc_free(identifier);
538 silc_pkcs_save_public_key(public_file, client->public_key,
540 silc_pkcs_save_private_key(private_file, client->private_key,
541 NULL, SILC_PKCS_FILE_BIN);
545 g_free(private_file);
549 static int my_silc_scheduler(void)
551 silc_schedule_one(0);
555 static CHATNET_REC *create_chatnet(void)
557 return g_malloc0(sizeof(CHATNET_REC));
560 static SERVER_SETUP_REC *create_server_setup(void)
562 return g_malloc0(sizeof(SERVER_SETUP_REC));
565 static CHANNEL_SETUP_REC *create_channel_setup(void)
567 return g_malloc0(sizeof(CHANNEL_SETUP_REC));
570 static SERVER_CONNECT_REC *create_server_connect(void)
572 return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
575 /* Command line option variables */
576 void silc_core_init(void)
578 CHAT_PROTOCOL_REC *rec;
580 silc_client = silc_client_alloc(&ops, NULL);
581 silc_client->username = g_strdup(settings_get_str("user_name"));
582 silc_client->hostname = silc_net_localhost();
583 silc_client->realname = g_strdup(settings_get_str("real_name"));
585 if (!load_keys(silc_client)) {
590 silc_client_init(silc_client);
592 rec = g_new0(CHAT_PROTOCOL_REC, 1);
594 rec->fullname = "Secure Internet Live Conferencing";
595 rec->chatnet = "silcnet";
597 rec->create_chatnet = create_chatnet;
598 rec->create_server_setup = create_server_setup;
599 rec->create_channel_setup = create_channel_setup;
600 rec->create_server_connect = create_server_connect;
602 rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
604 rec->channel_create =
605 (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
608 (QUERY_REC *(*) (const char *, const char *, int))
611 chat_protocol_register(rec);
615 silc_channels_init();
618 idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
621 void silc_core_deinit(void)
624 signal_emit("chat protocol deinit", 1,
625 chat_protocol_find("SILC"));
627 silc_server_deinit();
628 silc_channels_deinit();
629 silc_queries_deinit();
631 chat_protocol_unregister("SILC");
633 g_source_remove(idletag);
636 g_free(silc_client->username);
637 g_free(silc_client->realname);
638 silc_client_free(silc_client);