Fri Jan 02 10:28:15 CET 2004 Jochen Eisinger <jochen@penguin-breeder.org>
[silc.git] / apps / irssi / src / silc / core / silc-core.c
1 /*
2
3   silc-core.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2001 Pekka Riikonen
8
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.
13   
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.
18
19 */
20
21 #include "module.h"
22 #include "chat-protocols.h"
23 #include "args.h"
24
25 #include "chatnets.h"
26 #include "servers-setup.h"
27 #include "channels-setup.h"
28 #include "silc-servers.h"
29 #include "silc-channels.h"
30 #include "silc-queries.h"
31 #include "silc-nicklist.h"
32 #include "silc-chatnets.h"
33
34 #include "signals.h"
35 #include "levels.h"
36 #include "settings.h"
37 #include "fe-common/core/printtext.h"
38 #include "fe-common/core/fe-channels.h"
39 #include "fe-common/core/keyboard.h"
40 #include "fe-common/silc/module-formats.h"
41
42 /* Command line option variables */
43 static char *opt_pkcs = NULL;
44 static int opt_bits = 0;
45
46 static int idletag = -1;
47
48 SilcClient silc_client = NULL;
49 extern SilcClientOperations ops;
50 extern bool silc_debug;
51 extern bool silc_debug_hexdump;
52
53 void silc_expandos_init(void);
54 void silc_expandos_deinit(void);
55
56 void silc_lag_init(void);
57 void silc_lag_deinit(void);
58
59 static int my_silc_scheduler(void)
60 {
61   silc_client_run_one(silc_client);
62   return 1;
63 }
64
65 static CHATNET_REC *create_chatnet(void)
66 {
67   return g_malloc0(sizeof(CHATNET_REC));
68 }
69
70 static SERVER_SETUP_REC *create_server_setup(void)
71 {
72   return g_malloc0(sizeof(SERVER_SETUP_REC));
73 }
74
75 static CHANNEL_SETUP_REC *create_channel_setup(void)
76 {
77   return g_malloc0(sizeof(CHANNEL_SETUP_REC));
78 }
79
80 static SERVER_CONNECT_REC *create_server_connect(void)
81 {
82   return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
83 }
84
85 static void destroy_server_connect(SERVER_CONNECT_REC *conn)
86 {
87
88 }
89
90 /* Checks user information and saves them to the config file it they
91    do not exist there already. */
92
93 static void silc_init_userinfo(void)
94 {
95   const char *set, *nick, *user_name;
96   char *str;   
97         
98   /* check if nick/username/realname wasn't read from setup.. */
99   set = settings_get_str("real_name");
100   if (set == NULL || *set == '\0') {
101     str = g_getenv("SILCNAME");
102     if (!str)
103       str = g_getenv("IRCNAME");
104     settings_set_str("real_name",
105                      str != NULL ? str : silc_get_real_name());
106   }
107  
108   /* username */
109   user_name = settings_get_str("user_name");
110   if (user_name == NULL || *user_name == '\0') {
111     str = g_getenv("SILCUSER");
112     if (!str)
113       str = g_getenv("IRCUSER");
114     settings_set_str("user_name",
115                      str != NULL ? str : silc_get_username());
116
117     user_name = settings_get_str("user_name");
118   }
119
120   /* nick */
121   nick = settings_get_str("nick");
122   if (nick == NULL || *nick == '\0') {
123     str = g_getenv("SILCNICK");
124     if (!str)
125       str = g_getenv("IRCNICK");
126     settings_set_str("nick", str != NULL ? str : user_name);
127     
128     nick = settings_get_str("nick");
129   }
130                 
131   /* alternate nick */
132   set = settings_get_str("alternate_nick");
133   if (set == NULL || *set == '\0') {
134     str = g_strconcat(nick, "_", NULL);
135     settings_set_str("alternate_nick", str);
136     g_free(str);
137   }
138
139   /* host name */
140   set = settings_get_str("hostname");
141   if (set == NULL || *set == '\0') {
142     str = g_getenv("SILCHOST");
143     if (!str)
144       str = g_getenv("IRCHOST");
145     if (str != NULL)
146       settings_set_str("hostname", str);
147   }
148 }
149
150 #ifdef SILC_DEBUG
151 static bool i_debug;
152 static bool silc_irssi_debug_print(char *file, char *function, int line,
153                                    char *message, void *context)
154 {
155   printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
156             "DEBUG: %s:%d: %s", function, line, message);
157   return TRUE;
158 }
159 #endif
160
161 static void sig_setup_changed(void)
162 {
163 #ifdef SILC_DEBUG
164   bool debug = settings_get_bool("debug");
165   if (debug) {
166     const char *debug_string = settings_get_str("debug_string");
167     i_debug = silc_debug = TRUE;
168     if (strlen(debug_string))
169       silc_log_set_debug_string(debug_string);
170     silc_log_set_debug_callbacks(silc_irssi_debug_print, NULL, NULL, NULL);
171     return;
172   }
173   if (i_debug)
174     silc_debug = FALSE;
175 #endif
176 }
177
178 /* Log callbacks */
179
180 static bool silc_log_misc(SilcLogType type, char *message, void *context)
181 {
182   printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s: %s", 
183             (type == SILC_LOG_INFO ? "[Info]" :
184              type == SILC_LOG_WARNING ? "[Warning]" : "[Error]"), message);
185   return TRUE;
186 }
187
188 static void silc_nickname_format_parse(const char *nickname,
189                                        char **ret_nickname)
190 {
191   silc_parse_userfqdn(nickname, ret_nickname, NULL);
192 }
193
194 static void silc_register_cipher(SilcClient client, const char *cipher)
195 {
196   int i;
197
198   if (cipher) {
199     for (i = 0; silc_default_ciphers[i].name; i++)
200       if (!strcmp(silc_default_ciphers[i].name, cipher)) {
201         silc_cipher_register(&(silc_default_ciphers[i]));
202         break;
203       }
204     
205     if (!silc_cipher_is_supported(cipher)) {
206       SILC_LOG_ERROR(("Unknown cipher `%s'", cipher));
207       exit(1);
208     }
209   }
210
211   /* Register other defaults */
212   silc_cipher_register_default();
213 }
214
215 static void silc_register_hash(SilcClient client, const char *hash)
216 {
217   int i;
218
219   if (hash) {
220     for (i = 0; silc_default_hash[i].name; i++)
221       if (!strcmp(silc_default_hash[i].name, hash)) {
222         silc_hash_register(&(silc_default_hash[i]));
223         break;
224       }
225     
226     if (!silc_hash_is_supported(hash)) {
227       SILC_LOG_ERROR(("Unknown hash function `%s'", hash));
228       exit(1);
229     }
230   }
231
232   /* Register other defaults */
233   silc_hash_register_default();
234 }
235
236 static void silc_register_hmac(SilcClient client, const char *hmac)
237 {
238   int i;
239
240   if (hmac) {
241     for (i = 0; silc_default_hmacs[i].name; i++)
242       if (!strcmp(silc_default_hmacs[i].name, hmac)) {
243         silc_hmac_register(&(silc_default_hmacs[i]));
244         break;
245       }
246     
247     if (!silc_hmac_is_supported(hmac)) {
248       SILC_LOG_ERROR(("Unknown HMAC `%s'", hmac));
249       exit(1);
250     }
251   }
252
253   /* Register other defaults */
254   silc_hmac_register_default();
255 }
256
257 /* Finalize init. Init finish signal calls this. */
258
259 void silc_opt_callback(poptContext con, 
260                        enum poptCallbackReason reason,
261                        const struct poptOption *opt,
262                        const char *arg, void *data)
263 {
264   if (strcmp(opt->longName, "list-ciphers") == 0) {
265     silc_cipher_register_default();
266     silc_client_list_ciphers();
267     exit(0);
268   }
269
270   if (strcmp(opt->longName, "list-hash-funcs") == 0) {
271     silc_hash_register_default();
272     silc_client_list_hash_funcs();
273     exit(0);
274   }
275
276   if (strcmp(opt->longName, "list-hmacs") == 0) {
277     silc_hmac_register_default();
278     silc_client_list_hmacs();
279     exit(0);
280   }
281
282   if (strcmp(opt->longName, "list-pkcs") == 0) {
283     silc_pkcs_register_default();
284     silc_client_list_pkcs();
285     exit(0);
286   }
287
288   if (strcmp(opt->longName, "debug") == 0) {
289     silc_debug = TRUE;
290     silc_debug_hexdump = TRUE;
291     silc_log_set_debug_string(arg);
292 #ifndef SILC_DEBUG
293     fprintf(stdout, 
294             "Run-time debugging is not enabled. To enable it recompile\n"
295             "the client with --enable-debug configuration option.\n");
296     sleep(1);
297 #endif
298   }
299
300   if (strcmp(opt->longName, "create-key-pair") == 0) {
301     /* Create new key pair and exit */
302     silc_cipher_register_default();
303     silc_pkcs_register_default();
304     silc_hash_register_default();
305     silc_hmac_register_default();
306     silc_create_key_pair(opt_pkcs, opt_bits, NULL, NULL, NULL,
307                          NULL, NULL, NULL, NULL, TRUE);
308     exit(0);
309   }
310
311   if (strcmp(opt->longName, "passphrase-change") == 0) {
312     /* Change the passphrase of the private key file */
313     silc_cipher_register_default();
314     silc_pkcs_register_default();
315     silc_hash_register_default();
316     silc_hmac_register_default();
317     silc_change_private_key_passphrase(arg, NULL, NULL);
318     exit(0);
319   }
320
321   if (strcmp(opt->longName, "show-key") == 0) {
322     /* Dump the key */
323     silc_cipher_register_default();
324     silc_pkcs_register_default();
325     silc_hash_register_default();
326     silc_hmac_register_default();
327     silc_show_public_key((char *)arg);
328     exit(0);
329   }
330 }
331
332 static void sig_init_finished(void)
333 {
334   /* Check ~/.silc directory and public and private keys */
335   if (!silc_client_check_silc_dir())
336     exit(1);
337
338   /* Load public and private key */
339   if (!silc_client_load_keys(silc_client))
340     exit(1);
341
342   /* Initialize the SILC client */
343   if (!silc_client_init(silc_client))
344     exit(1);
345
346   /* register SILC scheduler */
347   idletag = g_timeout_add(5, (GSourceFunc) my_silc_scheduler, NULL);
348 }
349
350 /* Init SILC. Called from src/fe-text/silc.c */
351
352 void silc_core_init(void)
353 {
354   static struct poptOption silc_options[] = {
355     { NULL, '\0', POPT_ARG_CALLBACK, (void *)&silc_opt_callback, '\0', NULL },
356     { "list-ciphers", 0, POPT_ARG_NONE, NULL, 0,
357       "List supported ciphers", NULL },
358     { "list-hash-funcs", 0, POPT_ARG_NONE, NULL, 0,
359       "List supported hash functions", NULL },
360     { "list-hmacs", 0, POPT_ARG_NONE, NULL, 0,
361       "List supported HMACs", NULL },
362     { "list-pkcs", 0, POPT_ARG_NONE, NULL, 0,
363       "List supported PKCSs", NULL },
364 #ifdef SILC_DEBUG
365     { "debug", 'd', POPT_ARG_STRING, NULL, 0,
366       "Enable debugging", "STRING" },
367 #endif /* SILC_DEBUG */
368     { "create-key-pair", 'C', POPT_ARG_NONE, NULL, 0,
369       "Create new public key pair", NULL },
370     { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0,
371       "Set the PKCS of the public key pair (-C)", "PKCS" },
372     { "bits", 0, POPT_ARG_INT, &opt_bits, 0,
373       "Set the length of the public key pair (-C)", "VALUE" },
374     { "passphrase-change", 'P', POPT_ARG_STRING, NULL, 0,
375       "Change the passphrase of private key file", "FILE" },
376     { "show-key", 'S', POPT_ARG_STRING, NULL, 0,
377       "Show the contents of the public key", "FILE" },
378     { NULL, '\0', 0, NULL }
379   };
380
381   CHAT_PROTOCOL_REC *rec;
382   SilcClientParams params;
383   const char *def_cipher, *def_hash, *def_hmac;
384
385   args_register(silc_options);
386
387   /* Settings */
388   settings_add_bool("server", "skip_motd", FALSE);
389   settings_add_str("server", "alternate_nick", NULL);
390   settings_add_bool("server", "use_auto_addr", FALSE);
391   settings_add_str("server", "auto_bind_ip", "");
392   settings_add_str("server", "auto_public_ip", "");
393   settings_add_int("server", "auto_bind_port", 0);
394   settings_add_str("server", "crypto_default_cipher", SILC_DEFAULT_CIPHER);
395   settings_add_str("server", "crypto_default_hash", SILC_DEFAULT_HASH);
396   settings_add_str("server", "crypto_default_hmac", SILC_DEFAULT_HMAC);
397   settings_add_int("server", "key_exchange_timeout_secs", 120);
398   settings_add_int("server", "key_exchange_rekey_secs", 3600);
399   settings_add_int("server", "connauth_request_secs", 2);
400   settings_add_int("server", "heartbeat", 300);
401   settings_add_bool("server", "ignore_message_signatures", FALSE);
402   settings_add_str("server", "session_filename", "session.$chatnet");
403
404   /* Requested Attributes settings */
405   settings_add_bool("silc", "attr_allow", TRUE);
406   settings_add_str("silc", "attr_vcard", "");
407   settings_add_str("silc", "attr_services", "");
408   settings_add_str("silc", "attr_status_mood", "NORMAL");
409   settings_add_str("silc", "attr_status_text", "");
410   settings_add_str("silc", "attr_status_message", NULL);
411   settings_add_str("silc", "attr_preferred_language", "");
412   settings_add_str("silc", "attr_preferred_contact", "CHAT");
413   settings_add_bool("silc", "attr_timezone", TRUE);
414   settings_add_str("silc", "attr_geolocation", "");
415   settings_add_str("silc", "attr_device_info", NULL);
416   settings_add_str("silc", "attr_public_keys", "");
417
418 #ifdef SILC_DEBUG
419   settings_add_bool("debug", "debug", FALSE);
420   settings_add_str("debug", "debug_string", "");
421 #endif
422
423   signal_add("setup changed", (SIGNAL_FUNC) sig_setup_changed);
424   signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
425
426   silc_init_userinfo();
427
428   /* Initialize client parameters */
429   memset(&params, 0, sizeof(params));
430   strcat(params.nickname_format, "%n@%h%a");
431   params.nickname_parse = silc_nickname_format_parse;
432   params.rekey_secs = settings_get_int("key_exchange_rekey_secs");
433   params.connauth_request_secs = settings_get_int("connauth_request_secs");
434
435   /* Allocate SILC client */
436   silc_client = silc_client_alloc(&ops, &params, NULL, silc_version_string);
437
438   /* Get the ciphers and stuff from config file */
439   def_cipher = settings_get_str("crypto_default_cipher");
440   def_hash = settings_get_str("crypto_default_hash");
441   def_hmac = settings_get_str("crypto_default_hmac");
442   silc_register_cipher(silc_client, def_cipher);
443   silc_register_hash(silc_client, def_hash);
444   silc_register_hmac(silc_client, def_hmac);
445   silc_pkcs_register_default();
446
447   /* Get user information */
448   silc_client->username = g_strdup(settings_get_str("user_name"));
449   silc_client->nickname = g_strdup(settings_get_str("nick"));
450   silc_client->hostname = silc_net_localhost();
451   silc_client->realname = g_strdup(settings_get_str("real_name"));
452
453   silc_log_set_callback(SILC_LOG_INFO, silc_log_misc, NULL);
454   silc_log_set_callback(SILC_LOG_WARNING, silc_log_misc, NULL);
455   silc_log_set_callback(SILC_LOG_ERROR, silc_log_misc, NULL);
456   silc_log_set_callback(SILC_LOG_FATAL, silc_log_misc, NULL);
457
458   /* Register SILC to the irssi */
459   rec = g_new0(CHAT_PROTOCOL_REC, 1);
460   rec->name = "SILC";
461   rec->fullname = "Secure Internet Live Conferencing";
462   rec->chatnet = "silcnet";
463   rec->create_chatnet = create_chatnet;
464   rec->create_server_setup = create_server_setup;
465   rec->create_channel_setup = create_channel_setup;
466   rec->create_server_connect = create_server_connect;
467   rec->destroy_server_connect = destroy_server_connect;
468   rec->server_init_connect = silc_server_init_connect; 
469   rec->server_connect = silc_server_connect;
470   rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, 
471                                            const char *, int))
472     silc_channel_create;
473   rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
474     silc_query_create;
475   
476   chat_protocol_register(rec);
477   g_free(rec);
478
479   silc_server_init();
480   silc_channels_init();
481   silc_queries_init();
482   silc_expandos_init();
483   silc_lag_init();
484   silc_chatnets_init();
485
486   module_register("silc", "core");
487 }
488
489 /* Deinit SILC. Called from src/fe-text/silc.c */
490
491 void silc_core_deinit(void)
492 {
493   if (idletag != -1)
494     g_source_remove(idletag);
495   
496   signal_emit("chat protocol deinit", 1,
497         chat_protocol_find("SILC"));
498   signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed);
499   signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
500
501   silc_server_deinit();
502   silc_channels_deinit();
503   silc_queries_deinit();
504   silc_expandos_deinit();
505   silc_lag_deinit();
506   silc_chatnets_deinit();
507   
508   chat_protocol_unregister("SILC");
509   
510   g_free(silc_client->username);
511   g_free(silc_client->realname);
512   silc_free(silc_client->hostname);
513   silc_pkcs_free(silc_client->pkcs);
514   silc_pkcs_private_key_free(silc_client->private_key);
515   silc_pkcs_public_key_free(silc_client->public_key);
516   silc_client_free(silc_client);
517 }