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 /* Command line option variables */
40 static char *opt_server = NULL;
41 static int opt_port = 0;
42 static char *opt_nickname = NULL;
43 static char *opt_channel = NULL;
44 static char *opt_cipher = NULL;
45 static char *opt_public_key = NULL;
46 static char *opt_private_key = NULL;
47 static char *opt_config_file = NULL;
48 static bool opt_no_silcrc = FALSE;
50 static bool opt_create_keypair = FALSE;
51 static char *opt_pkcs = NULL;
52 static char *opt_keyfile = NULL;
53 static int opt_bits = 0;
56 static SilcClient silc_client;
57 extern SilcClientOperations ops;
59 static void silc_say(SilcClient client, SilcClientConnection conn,
62 SILC_SERVER_REC *server;
66 server = conn == NULL ? NULL : conn->context;
69 str = g_strdup_vprintf(msg, va);
70 printtext(server, "#silc", MSGLEVEL_CRAP, "%s", str);
75 /* Message for a channel. The `sender' is the nickname of the sender
76 received in the packet. The `channel_name' is the name of the channel. */
79 silc_channel_message(SilcClient client, SilcClientConnection conn,
80 SilcClientEntry sender, SilcChannelEntry channel,
81 SilcMessageFlags flags, char *msg)
83 SILC_SERVER_REC *server;
85 SILC_CHANNEL_REC *chanrec;
87 server = conn == NULL ? NULL : conn->context;
88 chanrec = silc_channel_find_entry(server, channel);
90 nick = silc_nicklist_find(chanrec, sender);
91 signal_emit("message public", 6, server, msg,
92 nick == NULL ? "(unknown)" : nick->nick,
93 nick == NULL ? NULL : nick->host,
97 /* Private message to the client. The `sender' is the nickname of the
98 sender received in the packet. */
101 silc_private_message(SilcClient client, SilcClientConnection conn,
102 SilcClientEntry sender, SilcMessageFlags flags,
105 SILC_SERVER_REC *server;
107 server = conn == NULL ? NULL : conn->context;
108 signal_emit("message private", 4, server, msg,
109 sender->nickname ? sender->nickname : "(unknown)",
110 sender->username ? sender->username : NULL);
113 /* Notify message to the client. The notify arguments are sent in the
114 same order as servers sends them. The arguments are same as received
115 from the server except for ID's. If ID is received application receives
116 the corresponding entry to the ID. For example, if Client ID is received
117 application receives SilcClientEntry. Also, if the notify type is
118 for channel the channel entry is sent to application (even if server
119 does not send it). */
126 #define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
127 static NOTIFY_REC notifies[] = {
128 { SILC_NOTIFY_TYPE_NONE, NULL },
129 { SILC_NOTIFY_TYPE_INVITE, "invite" },
130 { SILC_NOTIFY_TYPE_JOIN, "join" },
131 { SILC_NOTIFY_TYPE_LEAVE, "leave" },
132 { SILC_NOTIFY_TYPE_SIGNOFF, "signoff" },
133 { SILC_NOTIFY_TYPE_TOPIC_SET, "topic" },
134 { SILC_NOTIFY_TYPE_NICK_CHANGE, "nick" },
135 { SILC_NOTIFY_TYPE_CMODE_CHANGE, "cmode" },
136 { SILC_NOTIFY_TYPE_CUMODE_CHANGE, "cumode" },
137 { SILC_NOTIFY_TYPE_MOTD, "motd" }
140 static void silc_notify(SilcClient client, SilcClientConnection conn,
141 SilcNotifyType type, ...)
143 SILC_SERVER_REC *server;
146 server = conn == NULL ? NULL : conn->context;
149 if (type == SILC_NOTIFY_TYPE_NONE) {
150 /* some generic notice from server */
151 printtext(server, NULL, MSGLEVEL_CRAP, "%s",
152 (char *) va_arg(va, char *));
153 } else if (type < MAX_NOTIFY) {
154 /* send signal about the notify event */
157 g_snprintf(signal, sizeof(signal), "silc event %s",
158 notifies[type].name);
159 signal_emit(signal, 2, server, va);
162 printtext(server, NULL, MSGLEVEL_CRAP,
163 "Unknown notify %d", type);
168 /* Called to indicate that connection was either successfully established
169 or connecting failed. This is also the first time application receives
170 the SilcClientConnection objecet which it should save somewhere. */
173 silc_connect(SilcClient client, SilcClientConnection conn, int success)
175 SILC_SERVER_REC *server = conn->context;
178 server->connected = TRUE;
179 signal_emit("event connected", 1, server);
181 server->connection_lost = TRUE;
182 server->conn->context = NULL;
183 server_disconnect(SERVER(server));
187 /* Called to indicate that connection was disconnected to the server. */
190 silc_disconnect(SilcClient client, SilcClientConnection conn)
192 SILC_SERVER_REC *server = conn->context;
194 server->conn->context = NULL;
196 server->connection_lost = TRUE;
197 server_disconnect(SERVER(server));
200 /* Command handler. This function is called always in the command function.
201 If error occurs it will be called as well. `conn' is the associated
202 client connection. `cmd_context' is the command context that was
203 originally sent to the command. `success' is FALSE if error occured
204 during command. `command' is the command being processed. It must be
205 noted that this is not reply from server. This is merely called just
206 after application has called the command. Just to tell application
207 that the command really was processed. */
210 silc_command(SilcClient client, SilcClientConnection conn,
211 SilcClientCommandContext cmd_context, int success,
216 /* Command reply handler. This function is called always in the command reply
217 function. If error occurs it will be called as well. Normal scenario
218 is that it will be called after the received command data has been parsed
219 and processed. The function is used to pass the received command data to
222 `conn' is the associated client connection. `cmd_payload' is the command
223 payload data received from server and it can be ignored. It is provided
224 if the application would like to re-parse the received command data,
225 however, it must be noted that the data is parsed already by the library
226 thus the payload can be ignored. `success' is FALSE if error occured.
227 In this case arguments are not sent to the application. `command' is the
228 command reply being processed. The function has variable argument list
229 and each command defines the number and type of arguments it passes to the
230 application (on error they are not sent). */
233 silc_command_reply(SilcClient client, SilcClientConnection conn,
234 SilcCommandPayload cmd_payload, int success,
235 SilcCommand command, SilcCommandStatus status, ...)
238 SILC_SERVER_REC *server = conn->context;
239 SILC_CHANNEL_REC *chanrec;
242 va_start(va, status);
244 /*g_snprintf(signal, sizeof(signal), "silc command reply %s",
245 silc_commands[type]);
246 signal_emit(signal, 2, server, va);*/
249 case SILC_COMMAND_JOIN:
251 char *channel, *mode;
253 SilcChannelEntry channel_entry;
255 channel = va_arg(va, char *);
256 channel_entry = va_arg(va, SilcChannelEntry);
257 modei = va_arg(va, uint32);
258 mode = silc_client_chmode(modei, channel_entry);
260 chanrec = silc_channel_find(server, channel);
261 if (chanrec != NULL && !success)
262 channel_destroy(CHANNEL(chanrec));
263 else if (chanrec == NULL && success)
264 chanrec = silc_channel_create(server, channel, TRUE);
266 g_free_not_null(chanrec->mode);
267 chanrec->mode = g_strdup(mode == NULL ? "" : mode);
268 signal_emit("channel mode changed", 1, chanrec);
271 case SILC_COMMAND_NICK:
273 SilcClientEntry client = va_arg(va, SilcClientEntry);
276 old = g_strdup(server->nick);
277 server_change_nick(SERVER(server), client->nickname);
278 nicklist_rename_unique(SERVER(server),
279 server->conn->local_entry, server->nick,
280 client, client->nickname);
282 signal_emit("message own_nick", 4,
283 server, server->nick, old, "");
287 case SILC_COMMAND_USERS:
289 SilcChannelEntry channel;
290 SilcChannelUser user;
293 channel = va_arg(va, SilcChannelEntry);
294 chanrec = silc_channel_find_entry(server, channel);
298 silc_list_start(channel->clients);
299 while ((user = silc_list_get(channel->clients)) != NULL)
300 silc_nicklist_insert(chanrec, user, FALSE);
302 ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
303 nicklist_set_own(CHANNEL(chanrec), ownnick);
304 signal_emit("channel joined", 1, chanrec);
305 fe_channels_nicklist(CHANNEL(chanrec),
306 CHANNEL_NICKLIST_FLAG_ALL);
314 /* Verifies received public key. If user decides to trust the key it is
315 saved as public server key for later use. If user does not trust the
316 key this returns FALSE. */
318 static int silc_verify_public_key(SilcClient client,
319 SilcClientConnection conn,
320 SilcSocketType conn_type,
321 unsigned char *pk, uint32 pk_len,
322 SilcSKEPKType pk_type)
327 /* Asks passphrase from user on the input line. */
329 static unsigned char *silc_ask_passphrase(SilcClient client,
330 SilcClientConnection conn)
335 /* Find authentication method and authentication data by hostname and
336 port. The hostname may be IP address as well. The found authentication
337 method and authentication data is returned to `auth_meth', `auth_data'
338 and `auth_data_len'. The function returns TRUE if authentication method
339 is found and FALSE if not. `conn' may be NULL. */
342 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
343 char *hostname, uint16 port,
344 SilcProtocolAuthMeth *auth_meth,
345 unsigned char **auth_data,
346 uint32 *auth_data_len)
351 /* Notifies application that failure packet was received. This is called
352 if there is some protocol active in the client. The `protocol' is the
353 protocol context. The `failure' is opaque pointer to the failure
354 indication. Note, that the `failure' is protocol dependant and application
355 must explicitly cast it to correct type. Usually `failure' is 32 bit
356 failure type (see protocol specs for all protocol failure types). */
359 silc_failure(SilcClient client, SilcClientConnection conn,
360 SilcProtocol protocol, void *failure)
362 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
363 SilcSKEStatus status = (SilcSKEStatus)failure;
365 if (status == SILC_SKE_STATUS_BAD_VERSION)
366 silc_say(client, conn,
367 "You are running incompatible client version (it may be "
368 "too old or too new)");
369 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
370 silc_say(client, conn, "Server does not support your public key type");
371 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
372 silc_say(client, conn,
373 "Server does not support one of your proposed KE group");
374 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
375 silc_say(client, conn,
376 "Server does not support one of your proposed cipher");
377 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
378 silc_say(client, conn,
379 "Server does not support one of your proposed PKCS");
380 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
381 silc_say(client, conn,
382 "Server does not support one of your proposed hash function");
383 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
384 silc_say(client, conn,
385 "Server does not support one of your proposed HMAC");
386 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
387 silc_say(client, conn, "Incorrect signature");
390 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
391 uint32 err = (uint32)failure;
393 if (err == SILC_AUTH_FAILED)
394 silc_say(client, conn, "Authentication failed");
398 /* Asks whether the user would like to perform the key agreement protocol.
399 This is called after we have received an key agreement packet or an
400 reply to our key agreement packet. This returns TRUE if the user wants
401 the library to perform the key agreement protocol and FALSE if it is not
402 desired (application may start it later by calling the function
403 silc_client_perform_key_agreement). */
406 silc_key_agreement(SilcClient client, SilcClientConnection conn,
407 SilcClientEntry client_entry, char *hostname,
409 SilcKeyAgreementCallback *completion,
414 /* We will just display the info on the screen and return FALSE and user
415 will have to start the key agreement with a command. */
418 memset(host, 0, sizeof(host));
419 snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port);
422 silc_say(client, conn, "%s wants to perform key agreement %s",
423 client_entry->nickname, hostname ? host : "");
431 /* SILC client operations */
432 SilcClientOperations ops = {
434 silc_channel_message,
435 silc_private_message,
441 silc_get_auth_method,
442 silc_verify_public_key,
448 static int my_silc_scheduler(void)
450 silc_schedule_one(0);
454 static CHATNET_REC *create_chatnet(void)
456 return g_malloc0(sizeof(CHATNET_REC));
459 static SERVER_SETUP_REC *create_server_setup(void)
461 return g_malloc0(sizeof(SERVER_SETUP_REC));
464 static CHANNEL_SETUP_REC *create_channel_setup(void)
466 return g_malloc0(sizeof(CHANNEL_SETUP_REC));
469 static SERVER_CONNECT_REC *create_server_connect(void)
471 return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
474 void silc_core_init(void)
476 CHAT_PROTOCOL_REC *rec;
478 static struct poptOption options[] = {
479 { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0,
480 "Create new public key pair", NULL },
481 { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0,
482 "Set the PKCS of the public key pair", "PKCS" },
483 { "bits", 0, POPT_ARG_INT, &opt_bits, 0,
484 "Set the length of the public key pair", "VALUE" },
485 { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0,
486 "Show the contents of the public key", "FILE" },
487 { NULL, '\0', 0, NULL }
490 args_register(options);
492 if (opt_create_keypair == TRUE) {
493 /* Create new key pair and exit */
494 silc_cipher_register_default();
495 silc_pkcs_register_default();
496 silc_hash_register_default();
497 silc_hmac_register_default();
498 silc_client_create_key_pair(opt_pkcs, opt_bits,
499 NULL, NULL, NULL, NULL, NULL);
504 if (opt_keyfile == TRUE) {
506 silc_cipher_register_default();
507 silc_pkcs_register_default();
508 silc_hash_register_default();
509 silc_hmac_register_default();
510 silc_client_show_key(opt_keyfile);
511 silc_free(opt_keyfile);
515 silc_client = silc_client_alloc(&ops, NULL);
516 silc_client->username = g_strdup(settings_get_str("user_name"));
517 silc_client->hostname = silc_net_localhost();
518 silc_client->realname = g_strdup(settings_get_str("real_name"));
520 if (!load_keys(silc_client)) {
525 silc_client_init(silc_client);
527 rec = g_new0(CHAT_PROTOCOL_REC, 1);
529 rec->fullname = "Secure Internet Live Conferencing";
530 rec->chatnet = "silcnet";
531 rec->create_chatnet = create_chatnet;
532 rec->create_server_setup = create_server_setup;
533 rec->create_channel_setup = create_channel_setup;
534 rec->create_server_connect = create_server_connect;
535 rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
537 rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
539 rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
542 chat_protocol_register(rec);
546 silc_channels_init();
549 idletag = g_timeout_add(100, (GSourceFunc) my_silc_scheduler, NULL);
552 void silc_core_deinit(void)
555 signal_emit("chat protocol deinit", 1,
556 chat_protocol_find("SILC"));
558 silc_server_deinit();
559 silc_channels_deinit();
560 silc_queries_deinit();
562 chat_protocol_unregister("SILC");
564 g_source_remove(idletag);
567 g_free(silc_client->username);
568 g_free(silc_client->realname);
569 silc_client_free(silc_client);