Simplify password checking code.
[jelmer/ctrlproxy.git] / src / settings.c
1 /*
2         ctrlproxy: A modular IRC proxy
3         (c) 2002-2007 Jelmer Vernooij <jelmer@nl.linux.org>
4
5         This program is free software; you can redistribute it and/or modify
6         it under the terms of the GNU General Public License as published by
7         the Free Software Foundation; either version 3 of the License, or
8         (at your option) any later version.
9
10         This program is distributed in the hope that it will be useful,
11         but WITHOUT ANY WARRANTY; without even the implied warranty of
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13         GNU General Public License for more details.
14
15         You should have received a copy of the GNU General Public License
16         along with this program; if not, write to the Free Software
17         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20
21 #include "internals.h"
22 #include "ssl.h"
23 #include "keyfile.h"
24
25 #ifdef HAVE_SYS_SOCKET_H
26 #include <sys/socket.h>
27 #endif
28
29 #include <netdb.h>
30 #include <sys/socket.h>
31 #include <glib/gstdio.h>
32
33 #define DEFAULT_ADMIN_PORT 6680
34 #define DEFAULT_SOCKS_PORT 1080
35
36 #define CHANNEL_KEY_FILE_HEADER \
37         "; This file contains channel keys.\n" \
38         "; It has the same format as the ppp secrets files.\n" \
39         "; It should contain one entry per line, each entry consisting of: \n" \
40         "; a channel name, channel key and network name, separated by tabs.\n" \
41         ";\n\n"
42
43 static GList *known_keys = NULL;
44
45 static void config_cleanup_networks_dir(struct ctrlproxy_config *cfg);
46 static void config_save_log(struct log_file_config *data,
47                                                         struct ctrlproxy_config *config);
48 static void config_save_auto_away(struct auto_away_config *d, 
49                                                                   struct ctrlproxy_config *config);
50
51 static const char *builtin_known_keys[] = {
52         "autoconnect",
53         "autosave",
54         "auto-away-enable",
55         "auto-away-message",
56         "auto-away-nick",
57         "auto-away-client-limit",
58         "auto-away-time",
59         "create-implicit",
60         "max_who_age",
61         "replication",
62         "linestack",
63         "report-time",
64         "report-time-offset",
65         "motd-file",
66         "default-client-charset",
67         "learn-nickserv",
68         "learn-network-name",
69         "admin-log",
70         "admin-user",
71         "password",
72         "listener-auto",
73         "listener-autoport",
74         "logging",
75         "logfile",
76         "logdir",
77         "port",
78         "bind",
79         "ssl",
80         "keytab",
81         "default-network",
82         "log-logfilename",
83         "log-format-nickchange",
84         "log-format-topic",
85         "log-format-notopic",
86         "log-format-part",
87         "log-format-join",
88         "log-format-msg",
89         "log-format-notice",
90         "log-format-action",
91         "log-format-kick",
92         "log-format-quit",
93         "log-format-mode",
94         NULL
95 };
96
97 static gboolean config_known_key(const char *name)
98 {
99         int i;
100
101         if (g_list_find_custom(known_keys, name, (GCompareFunc)strcmp) != NULL)
102                 return TRUE;
103
104         for (i = 0; builtin_known_keys[i]; i++)
105                 if (!strcmp(builtin_known_keys[i], name))
106                         return TRUE;
107
108         return FALSE;
109 }
110
111 void config_register_known_key(char *name)
112 {
113         if (config_known_key(name))
114                 return;
115         known_keys = g_list_insert_sorted(known_keys, g_strdup(name), (GCompareFunc)strcmp);
116 }
117
118
119
120 gboolean g_key_file_save_to_file(GKeyFile *kf, const gchar *file, GError **error)
121 {
122         gsize length, nr;
123         char *data = g_key_file_to_data(kf, &length, error);
124         GIOChannel *gio;
125
126         if (!data)
127                 return FALSE;
128
129         gio = g_io_channel_new_file(file, "w+", error);
130         if (!gio) {
131                 g_free(data);
132                 return FALSE;
133         }
134         
135         g_io_channel_write_chars(gio, data, length, &nr, error);
136
137         g_free(data);
138
139         g_io_channel_unref(gio);
140
141         return TRUE;
142 }
143
144 static void config_save_tcp_servers(struct network_config *n, GKeyFile *kf)
145 {
146         GList *gl;
147         int i = 0; 
148         gchar **values = g_new0(gchar *, g_list_length(n->type_settings.tcp.servers)+1);
149         
150         for (gl = n->type_settings.tcp.servers; gl; gl = gl->next) {
151                 struct tcp_server_config *ts = gl->data;
152                 char *name = irc_create_url(ts->host, ts->port, ts->ssl);
153
154                 values[i] = name;
155
156                 if (ts->password)
157                         g_key_file_set_string(kf, name, "password", ts->password);
158                 else
159                         g_key_file_remove_key(kf, name, "password", NULL);
160
161                 if (ts->bind_address) {
162                         g_key_file_set_string(kf, name, "bind", ts->bind_address);
163                 } else 
164                         g_key_file_remove_key(kf, name, "bind", NULL);
165
166                 i++;
167         }
168
169         g_key_file_set_string_list(kf, "global", "servers", (const gchar **)values, i);
170
171         g_strfreev(values);
172 }
173
174 static void config_save_network(const char *dir, struct network_config *n, GList **channel_keys)
175 {
176         GList *gl;
177         GKeyFile *kf;
178         char *fn;
179         char **autojoin_list;
180         int autojoin_list_count;
181         
182         if (!n->keyfile) {
183                 n->keyfile = g_key_file_new();
184         }
185
186         kf = n->keyfile;
187
188         g_key_file_set_string(kf, "global", "fullname", n->fullname);
189         g_key_file_set_string(kf, "global", "nick", n->nick);
190         g_key_file_set_string(kf, "global", "username", n->username);
191         if (n->autocmd) {
192                 g_key_file_set_string_list(kf, "global", "autocmd", n->autocmd,
193                                                            g_strv_length(n->autocmd));
194         } else {
195                 g_key_file_remove_key(kf, "global", "autocmd", NULL);
196         }
197
198         if (n->queue_speed)
199                 g_key_file_set_integer(kf, "global", "queue-speed", n->queue_speed);
200         if (n->reconnect_interval != -1)
201                 g_key_file_set_integer(kf, "global", "reconnect-interval", n->reconnect_interval);
202
203         switch(n->type) {
204         case NETWORK_VIRTUAL:
205                 g_key_file_set_string(kf, "global", "virtual", n->type_settings.virtual_type);
206                 break;
207         case NETWORK_PROGRAM:
208                 g_key_file_set_string(kf, "global", "program", n->type_settings.program_location);
209                 break;
210         case NETWORK_TCP:
211                 config_save_tcp_servers(n, kf);
212                 if (n->type_settings.tcp.default_bind_address != NULL)
213                         g_key_file_set_string(kf, "global", "bind", 
214                                                                   n->type_settings.tcp.default_bind_address);
215                 else
216                         g_key_file_remove_key(kf, "global", "bind", NULL);
217                 break;
218         default:break;
219         }
220
221         autojoin_list = g_new0(char *, g_list_length(n->channels));
222         autojoin_list_count = 0;
223
224         for (gl = n->channels; gl; gl = gl->next) {
225                 struct channel_config *c = gl->data;
226                 struct keyfile_entry *key;
227
228                 if (c->key) {
229                         key = g_new0(struct keyfile_entry, 1);
230                         key->network = n->name;
231                         key->nick = c->name;
232                         key->pass = c->key;
233
234                         *channel_keys = g_list_append(*channel_keys, key);
235                 }
236                 
237                 if (c->autojoin) {
238                         autojoin_list[autojoin_list_count] = c->name;
239                         autojoin_list_count++;
240                 }
241
242                 g_key_file_remove_group(kf, c->name, NULL);
243         }
244
245         if (autojoin_list == NULL)
246                 g_key_file_remove_key(kf, "global", "autojoin", NULL);
247         else
248                 g_key_file_set_string_list(kf, "global", "autojoin", (const gchar **)autojoin_list, 
249                                                            autojoin_list_count);
250
251         g_free(autojoin_list);
252
253         fn = g_build_filename(dir, n->name, NULL);
254         g_key_file_save_to_file(kf, fn, NULL);
255         g_free(fn);
256 }
257
258 static void config_save_listeners(struct ctrlproxy_config *cfg, const char *path)
259 {
260         GList *gl;
261         char *filename;
262         GKeyFile *kf; 
263         GError *error = NULL;
264         gboolean empty = TRUE;
265
266         if (cfg->password != NULL)
267                 g_key_file_set_string(cfg->keyfile, "global", "password", cfg->password);
268
269         if (cfg->auto_listener) {
270                 g_key_file_set_boolean(cfg->keyfile, "global", "listener-auto", cfg->auto_listener);
271                 g_key_file_set_integer(cfg->keyfile, "global", "listener-autoport", cfg->listener_autoport);
272         }
273
274         filename = g_build_filename(path, "listener", NULL);
275
276         kf = g_key_file_new();
277
278         for (gl = cfg->listeners; gl; gl = gl->next) {
279                 struct listener_config *l = gl->data;
280
281                 if (l->is_default) {
282                         g_key_file_set_string(cfg->keyfile, "global", "port", l->port);
283                         if (l->address != NULL)
284                                 g_key_file_set_string(cfg->keyfile, "global", "bind", l->address);
285                         if (l->password != NULL)
286                                 g_key_file_set_string(cfg->keyfile, "global", "password", l->password);
287
288                         if (g_key_file_has_key(cfg->keyfile, "global", "ssl", NULL) || l->ssl)
289                                 g_key_file_set_boolean(cfg->keyfile, "global", "ssl", l->ssl);
290
291                         if (l->network != NULL)
292                                 g_key_file_set_string(cfg->keyfile, "global", "default-network",
293                                                                   l->network);
294                         cfg->default_listener = l;
295                 } else {
296                         char *tmp;
297                         empty = FALSE;
298                         if (!l->address) 
299                                 tmp = g_strdup(l->port);
300                         else
301                                 tmp = g_strdup_printf("%s:%s", l->address, l->port);
302
303                         if (l->password != NULL)
304                                 g_key_file_set_string(kf, tmp, "password", l->password);
305
306                         if (l->network != NULL) {
307                                 g_key_file_set_string(kf, tmp, "network", l->network);
308                         }
309
310                         g_key_file_set_boolean(kf, tmp, "ssl", l->ssl);
311                 
312                         g_free(tmp);
313                 }
314         }
315
316         if (empty) {
317                 unlink(filename);
318         } else { 
319                 if (!g_key_file_save_to_file(kf, filename, &error)) {
320                         log_global(LOG_WARNING, "Unable to save to \"%s\": %s", filename, error->message);
321                 }
322         }
323         
324         g_free(filename);
325 }
326
327 static void config_save_networks(struct ctrlproxy_config *cfg, const char *config_dir, GList *networks)
328 {
329         char *networksdir = g_build_filename(config_dir, "networks", NULL);
330         GList *gl;
331         GList *channel_keys = NULL;
332
333         if (!g_file_test(networksdir, G_FILE_TEST_IS_DIR)) {
334                 if (g_mkdir(networksdir, 0700) != 0) {
335                         log_global(LOG_ERROR, "Can't create networks directory '%s': %s", networksdir, strerror(errno));
336                         return;
337                 }
338         }
339
340         for (gl = networks; gl; gl = gl->next) {
341                 struct network_config *n = gl->data;
342                 if (!n->implicit) 
343                         config_save_network(networksdir, n, &channel_keys);
344         }
345
346         config_cleanup_networks_dir(cfg);
347
348         if (channel_keys != NULL) {
349                 char *filename = g_build_filename(cfg->config_dir, "keys", 
350                                                                           NULL);
351                 keyfile_write_file(channel_keys, CHANNEL_KEY_FILE_HEADER, filename);
352                 g_free(filename);
353
354                 while (channel_keys) {
355                         g_free(channel_keys->data);
356                         channel_keys = channel_keys->next;
357                 }
358
359                 g_list_free(channel_keys);
360         }
361
362         g_free(networksdir);
363 }
364
365 /**
366  * Save configuration to a configuration directory.
367  *
368  * @param cfg The configuration to save.
369  * @param configuration_dir Directory to save to.
370  */
371 void save_configuration(struct ctrlproxy_config *cfg, const char *configuration_dir)
372 {
373         char *fn, **list;
374         int i;
375         GList *gl;
376
377         if (!g_file_test(configuration_dir, G_FILE_TEST_IS_DIR)) {
378                 if (g_mkdir(configuration_dir, 0700) != 0) {
379                         log_global(LOG_ERROR, "Unable to open configuration directory '%s'\n", configuration_dir);
380                         return;
381                 }
382         }
383
384         if (!cfg->keyfile)
385                 cfg->keyfile = g_key_file_new();
386
387         g_key_file_set_boolean(cfg->keyfile, "global", "autosave", cfg->autosave);
388         if (cfg->admin_user != NULL)
389                 g_key_file_set_string(cfg->keyfile, "global", "admin-user", cfg->admin_user);
390
391         if (g_key_file_has_key(cfg->keyfile, "global", "admin-log", NULL) ||
392                 !cfg->admin_log)
393                 g_key_file_set_boolean(cfg->keyfile, "global", "admin-log", cfg->admin_log);
394
395         if (g_key_file_has_key(cfg->keyfile, "global", "max_who_age", NULL) ||
396                 cfg->max_who_age != 0)
397                 g_key_file_set_integer(cfg->keyfile, "global", "max_who_age", cfg->max_who_age);
398
399         if (g_key_file_has_key(cfg->keyfile, "global", "learn-nickserv", NULL) ||
400                 !cfg->learn_nickserv)
401                 g_key_file_set_boolean(cfg->keyfile, "global", "learn-nickserv", cfg->learn_nickserv);
402
403         if (g_key_file_has_key(cfg->keyfile, "global", "learn-network-name", NULL) ||
404                 !cfg->learn_network_name)
405                 g_key_file_set_boolean(cfg->keyfile, "global", "learn-network-name", cfg->learn_network_name);
406
407         if (g_key_file_has_key(cfg->keyfile, "global", "create-implicit", NULL) || !cfg->create_implicit)
408                 g_key_file_set_boolean(cfg->keyfile, "global", "create-implicit", cfg->create_implicit);
409
410         if (cfg->client_charset != NULL)
411                 g_key_file_set_string(cfg->keyfile, "global", "default-client-charset", cfg->client_charset);
412         
413         if (cfg->replication)
414                 g_key_file_set_string(cfg->keyfile, "global", "replication", cfg->replication);
415         if (cfg->linestack_backend) 
416                 g_key_file_set_string(cfg->keyfile, "global", "linestack", cfg->linestack_backend);
417         if (cfg->motd_file != NULL)
418                 g_key_file_set_string(cfg->keyfile, "global", "motd-file", cfg->motd_file);
419
420         switch (cfg->report_time) {
421         case REPORT_TIME_ALWAYS:
422                 g_key_file_set_string(cfg->keyfile, "global", "report-time", 
423                                                           "always");
424                 break;
425         case REPORT_TIME_NEVER:
426                 g_key_file_set_string(cfg->keyfile, "global", "report-time", 
427                                                           "never");
428                 break;
429         case REPORT_TIME_REPLICATION:
430                 g_key_file_set_string(cfg->keyfile, "global", "report-time", 
431                                                           "replication");
432                 break;
433         }
434
435         if (cfg->report_time_offset != 0 ||
436                 g_key_file_has_key(cfg->keyfile, "global", "report-time-offset", NULL))
437                 g_key_file_set_integer(cfg->keyfile, "global", "report-time-offset", cfg->report_time_offset);
438
439         config_save_networks(cfg, configuration_dir, cfg->networks);
440
441         config_save_listeners(cfg, configuration_dir);
442
443         config_save_log(cfg->log_file, cfg);
444
445         config_save_auto_away(&cfg->auto_away, cfg);
446
447         i = 0;
448         list = g_new0(char *, g_list_length(cfg->networks)+1);
449         for (gl = cfg->networks; gl; gl = gl->next) {
450                 struct network_config *nc = gl->data;
451
452                 if (nc->autoconnect) {
453                         list[i] = nc->name;
454                         i++;
455                 }
456         }
457         
458         if (i > 0) 
459                 g_key_file_set_string_list(cfg->keyfile, "global", "autoconnect", (const gchar **)list, i);
460
461         g_free(list);
462
463         fn = g_build_filename(configuration_dir, "config", NULL);
464         g_key_file_save_to_file(cfg->keyfile, fn, NULL);
465         g_free(fn);
466 }
467
468 static void config_load_channel(struct network_config *n, GKeyFile *kf, const char *name)
469 {
470         struct channel_config *ch = g_new0(struct channel_config, 1);
471
472         ch->name = g_strdup(name);
473         if (g_key_file_has_key(kf, name, "key", NULL)) 
474                 ch->key = g_key_file_get_string(kf, name, "key", NULL);
475
476         if (g_key_file_has_key(kf, name, "autojoin", NULL))
477                 ch->autojoin = g_key_file_get_boolean(kf, name, "autojoin", NULL);
478         
479         n->channels = g_list_append(n->channels, ch);
480 }
481
482 static void config_load_servers(struct network_config *n)
483 {
484         gsize size;
485         char **servers;
486         int i;
487         
488         servers = g_key_file_get_string_list(n->keyfile, "global", "servers", &size, NULL);
489
490         if (!servers)
491                 return;
492
493         for (i = 0; i < size; i++) {
494                 struct tcp_server_config *s = g_new0(struct tcp_server_config, 1);
495
496                 irc_parse_url(servers[i], &s->host, &s->port, &s->ssl);
497                 
498                 s->password = g_key_file_get_string(n->keyfile, servers[i], "password", NULL);
499                 if (g_key_file_has_key(n->keyfile, servers[i], "ssl", NULL))
500                         s->ssl = g_key_file_get_boolean(n->keyfile, servers[i], "ssl", NULL);
501
502                 s->bind_address = g_key_file_get_string(n->keyfile, servers[i], "bind", NULL);
503
504                 n->type_settings.tcp.servers = g_list_append(n->type_settings.tcp.servers, s);
505
506                 g_key_file_remove_group(n->keyfile, servers[i], NULL);
507         }
508
509         g_strfreev(servers);
510 }
511
512 static struct channel_config *config_find_add_channel(struct network_config *nc, const char *name)
513 {
514         GList *gl;
515         struct channel_config *cc;
516
517         for (gl = nc->channels; gl; gl = gl->next) { 
518                 cc = gl->data;
519                 if (!strcasecmp(cc->name, name)) 
520                         return cc;
521
522         }
523
524         cc = g_new0(struct channel_config, 1);
525         cc->name = g_strdup(name);
526
527         nc->channels = g_list_append(nc->channels, cc);
528
529         return cc;
530 }
531
532 static struct network_config *config_load_network(struct ctrlproxy_config *cfg, const char *dirname, 
533                                                                                                   const char *name, GList *channel_keys)
534 {
535         GKeyFile *kf;
536         struct network_config *n;
537         char *filename;
538         GList *gl;
539         int i;
540         char **groups;
541         GError *error = NULL;
542         gsize size;
543
544         kf = g_key_file_new();
545
546         filename = g_build_filename(dirname, name, NULL);
547
548         if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {       
549                 log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", filename, error->message);
550                 g_free(filename);
551                 g_key_file_free(kf);
552                 return NULL;
553         }       
554
555         n = network_config_init(cfg);
556         n->keyfile = kf;
557
558         g_free(filename);
559
560         if (g_key_file_has_key(kf, "global", "fullname", NULL)) {
561                 g_free(n->fullname);
562                 n->fullname = g_key_file_get_string(kf, "global", "fullname", NULL);
563                 if (!strcmp(n->fullname, "") || n->fullname[0] == ' ')
564                         log_global(LOG_WARNING, "Invalid fullname `%s' set for network `%s'", n->fullname, n->name);
565         }
566
567         if (g_key_file_has_key(kf, "global", "nick", NULL)) {
568                 g_free(n->nick);
569                 n->nick = g_key_file_get_string(kf, "global", "nick", NULL);
570                 if (!strcmp(n->nick, "") || n->nick[0] == ' ')
571                         log_global(LOG_WARNING, "Invalid nick name `%s' set for `%s'", n->nick, n->name);
572         }
573
574         if (g_key_file_has_key(kf, "global", "reconnect-interval", NULL)) {
575                 n->reconnect_interval = g_key_file_get_integer(kf, "global", "reconnect-interval", NULL);
576         }
577
578         if (g_key_file_has_key(kf, "global", "queue-speed", NULL)) {
579                 n->queue_speed = g_key_file_get_integer(kf, "global", "queue-speed", NULL);
580         }
581
582         if (g_key_file_has_key(kf, "global", "username", NULL)) {
583                 g_free(n->username);
584                 n->username = g_key_file_get_string(kf, "global", "username", NULL);
585                 if (!strcmp(n->username, "") || n->username[0] == ' ')
586                         log_global(LOG_WARNING, "Invalid username `%s' set for network `%s'", n->username, n->name);
587         }
588
589         if (g_key_file_has_key(kf, "global", "ignore_first_nick", NULL)) {
590                 n->ignore_first_nick = g_key_file_get_boolean(kf, "global", "ignore_first_nick", NULL);
591         }
592
593         if (g_key_file_has_key(kf, "global", "password", NULL)) {
594                 g_free(n->password);
595                 n->password = g_key_file_get_string(kf, "global", "password", NULL);
596         }
597
598         if (g_key_file_has_key(kf, "global", "autocmd", NULL)) {
599                 g_strfreev(n->autocmd);
600                 n->autocmd = g_key_file_get_string_list(n->keyfile, "global", "autocmd", &size, NULL);
601         }
602
603         n->name = g_strdup(name);
604
605         if (g_key_file_has_key(kf, "global", "program", NULL)) 
606                 n->type = NETWORK_PROGRAM;
607         else if (g_key_file_has_key(kf, "global", "virtual", NULL)) 
608                 n->type = NETWORK_VIRTUAL;
609         else 
610                 n->type = NETWORK_TCP;
611
612         switch (n->type) {
613         case NETWORK_TCP:
614                 n->type_settings.tcp.default_bind_address = g_key_file_get_string(kf, "global", "bind", NULL);
615                 config_load_servers(n);
616                 break;
617         case NETWORK_PROGRAM:
618                 n->type_settings.program_location = g_key_file_get_string(kf, "global", "program", NULL);
619                 break;
620         case NETWORK_VIRTUAL:
621                 n->type_settings.virtual_type = g_key_file_get_string(kf, "global", "virtual", NULL);
622                 break;
623         case NETWORK_IOCHANNEL:
624                 /* Don't store */
625                 break;
626         }
627
628         groups = g_key_file_get_groups(kf, &size);
629         for (i = 0; i < size; i++) {
630                 if (!g_ascii_isalpha(groups[i][0]))
631                         config_load_channel(n, kf, groups[i]);
632         }
633
634         g_strfreev(groups);
635
636         for (gl = channel_keys; gl; gl = gl->next) {
637                 struct keyfile_entry *ke = gl->data;
638
639                 if (!strcasecmp(ke->network, n->name)) {
640                         struct channel_config *cc = config_find_add_channel(n, n->nick);
641
642                         g_free(cc->key);
643                         cc->key = g_strdup(n->password);
644                 }
645         }
646
647         if (g_key_file_has_key(n->keyfile, "global", "autojoin", NULL)) {
648                 char **autojoin_channels;
649                 autojoin_channels = g_key_file_get_string_list(n->keyfile, "global", "autojoin", &size, NULL);
650                 for (i = 0; i < size; i++) {
651                         struct channel_config *cc = config_find_add_channel(n, autojoin_channels[i]);
652
653                         cc->autojoin = TRUE;
654                 }
655
656                 g_strfreev(autojoin_channels);
657         }
658
659         return n;
660 }
661
662 static struct network_config *find_create_network_config(struct ctrlproxy_config *cfg, const char *name)
663 {
664         GList *gl;
665         struct network_config *nc;
666         struct tcp_server_config *tc;
667
668         for (gl = cfg->networks; gl; gl = gl->next) {
669                 GList *gl1;
670                 nc = gl->data;
671
672                 if (g_strcasecmp(nc->name, name) == 0)
673                         return nc;
674
675                 if (nc->type != NETWORK_TCP) 
676                         continue;
677
678                 for (gl1 = nc->type_settings.tcp.servers; gl1; gl1 = gl1->next) {
679                         char *tmp;
680                         struct tcp_server_config *sc = gl1->data;
681
682                         if (g_strcasecmp(sc->host, name) == 0)
683                                 return nc;
684
685                         if (g_strncasecmp(sc->host, name, strlen(sc->host)) != 0)
686                                 continue;
687
688                         tmp = irc_create_url(sc->host, sc->port, FALSE);
689
690                         if (g_strcasecmp(tmp, name) == 0)
691                                 return nc;
692
693                         g_free(tmp);
694                 }
695         }
696
697         nc = network_config_init(cfg);
698         nc->name = g_strdup(name);
699         nc->autoconnect = FALSE;
700         nc->reconnect_interval = -1;
701         nc->type = NETWORK_TCP;
702         tc = g_new0(struct tcp_server_config, 1);
703         irc_parse_url(name, &tc->host, &tc->port, &tc->ssl);
704         nc->type_settings.tcp.servers = g_list_append(nc->type_settings.tcp.servers, tc);
705
706         cfg->networks = g_list_append(cfg->networks, nc);
707
708         return nc;
709 }
710
711 static void config_load_listeners_socks(struct ctrlproxy_config *cfg)
712 {
713         char **allows;
714         gsize size, i;
715         GKeyFile *kf = cfg->keyfile;
716         struct listener_config *l;
717
718         allows = g_key_file_get_string_list(kf, "socks", "allow", &size, NULL);
719
720         if (allows == NULL)
721                 return;
722
723         g_key_file_remove_key(kf, "socks", "allow", NULL);
724
725         l = g_new0(struct listener_config, 1);
726
727         if (g_key_file_has_key(kf, "socks", "port", NULL)) 
728                 l->port = g_key_file_get_string(kf, "socks", "port", NULL);
729         else 
730                 l->port = g_strdup_printf("%d", DEFAULT_SOCKS_PORT);
731
732         /* We can use the socks listener as default listener, if there was 
733          * no default listener specified */
734         if (cfg->listeners == NULL ||
735                 !((struct listener_config *)cfg->listeners->data)->is_default)
736                 l->is_default = TRUE;
737
738         g_key_file_remove_key(kf, "socks", "port", NULL);
739
740         for (i = 0; i < size; i++) {
741                 struct allow_rule *r = g_new0(struct allow_rule, 1);
742                 char **parts = g_strsplit(allows[i], ":", 2);
743                                         
744                 r->username = parts[0];
745                 r->password = parts[1];
746
747                 g_free(parts);
748                 l->allow_rules = g_list_append(l->allow_rules, r);
749         }
750
751         g_key_file_remove_group(kf, "socks", NULL);
752
753         g_strfreev(allows);
754
755         cfg->listeners = g_list_append(cfg->listeners, l);
756 }
757
758 static void config_load_listeners(struct ctrlproxy_config *cfg)
759 {
760         char *filename = g_build_filename(cfg->config_dir, "listener", NULL);
761         int i;
762         char **groups;
763         gsize size;
764         GKeyFile *kf;
765         GError *error = NULL;
766
767         if (g_key_file_has_key(cfg->keyfile, "listener", "pasword", NULL)) {
768                 g_key_file_set_string(cfg->keyfile, "global", "password", 
769                                                           g_key_file_get_string(cfg->keyfile, "listener", "password", NULL));
770                 g_key_file_remove_key(cfg->keyfile, "listener", "password", NULL);
771         }
772         cfg->password = g_key_file_get_string(cfg->keyfile, "global", "password", NULL);
773
774         if (g_key_file_has_key(cfg->keyfile, "global", "listener-auto", NULL)) {
775                 cfg->auto_listener = g_key_file_get_boolean(cfg->keyfile, "global", "listener-auto", NULL);
776         } else if (g_key_file_has_key(cfg->keyfile, "listener", "auto", NULL)) {
777                 cfg->auto_listener = g_key_file_get_boolean(cfg->keyfile, "listener", "auto", NULL);
778                 g_key_file_remove_key(cfg->keyfile, "listener", "auto", NULL);
779         }
780
781         if (g_key_file_has_key(cfg->keyfile, "global", "listener-autoport", NULL)) {
782                 cfg->listener_autoport = g_key_file_get_integer(cfg->keyfile, "global", "listener-autoport", NULL);
783         } else if (g_key_file_has_key(cfg->keyfile, "listener", "autoport", NULL)) {
784                 cfg->listener_autoport = g_key_file_get_integer(cfg->keyfile, "listener", "autoport", NULL);
785                 g_key_file_remove_key(cfg->keyfile, "listener", "autoport", NULL);
786         }
787
788         if (g_key_file_has_key(cfg->keyfile, "global", "port", NULL)) {
789                 struct listener_config *l = g_new0(struct listener_config, 1);
790                 l->port = g_key_file_get_string(cfg->keyfile, "global", "port", NULL);
791                 l->password = g_key_file_get_string(cfg->keyfile, "global", "password", NULL);
792                 l->address = g_key_file_get_string(cfg->keyfile, "global", "bind", NULL);
793                 l->ssl = g_key_file_has_key(cfg->keyfile, "global", "ssl", NULL) &&
794                                  g_key_file_get_boolean(cfg->keyfile, "global", "ssl", NULL);
795                 l->is_default = TRUE;
796
797                 l->network = g_key_file_get_string(cfg->keyfile, "global", "default-network", NULL);
798
799                 cfg->listeners = g_list_append(cfg->listeners, l);
800         }
801
802         kf = g_key_file_new();
803
804         if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {
805                 if (error->code != G_FILE_ERROR_NOENT)
806                         log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", filename, error->message);
807                 g_free(filename);
808                 return;
809         }
810                 
811         groups = g_key_file_get_groups(kf, &size);
812
813         for (i = 0; i < size; i++)
814         {
815                 struct listener_config *l;
816                 char *tmp;
817                 
818                 l = g_new0(struct listener_config, 1);
819
820                 tmp = g_strdup(groups[i]);
821                 l->port = strrchr(tmp, ':');
822                 if (l->port != NULL) {
823                         l->address = tmp;
824                         *l->port = '\0';
825                         l->port++;
826                 } else {
827                         l->port = tmp;
828                         l->address = NULL;
829                 }
830
831                 l->password = g_key_file_get_string(kf, groups[i], "password", NULL);
832
833                 if (g_key_file_has_key(kf, groups[i], "ssl", NULL))
834                         l->ssl = g_key_file_get_boolean(kf, groups[i], "ssl", NULL);
835
836 #ifdef HAVE_GNUTLS
837                 if (l->ssl)
838                         l->ssl_credentials = ssl_create_server_credentials(cfg, kf, groups[i]);
839 #endif
840
841                 if (g_key_file_has_key(kf, groups[i], "network", NULL))
842                         l->network = g_key_file_get_string(kf, groups[i], "network", NULL);
843
844                 cfg->listeners = g_list_append(cfg->listeners, l);
845         }
846
847         g_strfreev(groups);
848         g_free(filename);
849 }
850
851
852 struct network_config *config_find_network(struct ctrlproxy_config *cfg, 
853                                                                                    const char *name)
854 {
855         GList *gl;
856         for (gl = cfg->networks; gl; gl = gl->next) {
857                 struct network_config *nc = gl->data;
858                 if (!g_strcasecmp(nc->name, name))
859                         return nc;
860         }
861         return NULL;
862 }
863
864 void config_del_network(struct ctrlproxy_config *cfg, const char *name)
865 {
866         cfg->networks = g_list_remove(cfg->networks, config_find_network(cfg, name));
867 }
868
869 #define IS_SPECIAL_FILE(name) (name[0] == '.' || name[strlen(name)-1] == '~')
870
871 static void config_cleanup_networks_dir(struct ctrlproxy_config *cfg)
872 {
873         char *networksdir = g_build_filename(cfg->config_dir, "networks", NULL);
874         GDir *dir;
875         const char *name;
876
877         dir = g_dir_open(networksdir, 0, NULL);
878         if (dir == NULL)
879                 return;
880
881         while ((name = g_dir_read_name(dir))) {
882                 char *path;
883                 if (IS_SPECIAL_FILE(name) || config_find_network(cfg, name))
884                         continue;
885
886                 path = g_build_filename(networksdir, name, NULL);
887                 g_unlink(path);
888                 g_free(path);
889         }
890
891         g_free(networksdir);
892
893         g_dir_close(dir);
894 }
895
896 static void config_load_networks(struct ctrlproxy_config *cfg, GList *channel_keys)
897 {
898         char *networksdir = g_build_filename(cfg->config_dir, "networks", NULL);
899         GDir *dir;
900         const char *name;
901
902         dir = g_dir_open(networksdir, 0, NULL);
903         if (dir == NULL)
904                 return;
905
906         while ((name = g_dir_read_name(dir))) {
907                 if (IS_SPECIAL_FILE(name))
908                         continue;
909                 config_load_network(cfg, networksdir, name, channel_keys);
910         }
911
912         g_free(networksdir);
913
914         g_dir_close(dir);
915 }
916
917 #define FETCH_SETTING(data, kf, section, prefix, name) (data)->name = g_key_file_get_string((kf), (section), prefix __STRING(name), NULL)
918 #define STORE_SETTING(data, kf, section, prefix, name) g_key_file_set_string((kf), (section), prefix __STRING(name), (data)->name)
919
920 static void config_save_log(struct log_file_config *data,
921                                                         struct ctrlproxy_config *config)
922 {
923         if (data == NULL) {
924                 g_key_file_set_string(config->keyfile, "global", "logging", "none");
925                 return;
926         }
927
928         if (data->is_irssi) {
929                 g_key_file_set_string(config->keyfile, "global", "logging", "irssi");
930                 if (data->logbasedir) {
931                         if (!data->logbasedir_is_default)
932                                 g_key_file_set_string(config->keyfile, "global", "logdir", data->logbasedir);
933                 } else {
934                         g_key_file_set_string(config->keyfile, "global", "logfile", data->logfilename);
935                 }
936         } else {
937                 STORE_SETTING(data, config->keyfile, "global", "", logfilename);
938                 STORE_SETTING(data, config->keyfile, "global", "log-format-", nickchange);
939                 STORE_SETTING(data, config->keyfile, "global", "log-format-", topic);
940                 STORE_SETTING(data, config->keyfile, "global", "log-format-", notopic);
941                 STORE_SETTING(data, config->keyfile, "global", "log-format-", part);
942                 STORE_SETTING(data, config->keyfile, "global", "log-format-", join);
943                 STORE_SETTING(data, config->keyfile, "global", "log-format-", msg);
944                 STORE_SETTING(data, config->keyfile, "global", "log-format-", notice);
945                 STORE_SETTING(data, config->keyfile, "global", "log-format-", action);
946                 STORE_SETTING(data, config->keyfile, "global", "log-format-", kick);
947                 STORE_SETTING(data, config->keyfile, "global", "log-format-", quit);
948                 STORE_SETTING(data, config->keyfile, "global", "log-format-", mode);
949         }
950 }
951
952 static void config_load_log(struct ctrlproxy_config *config)
953 {
954         GKeyFile *kf = config->keyfile;
955         struct log_file_config *data;
956         char *logging = NULL;
957
958         if (g_key_file_has_key(kf, "global", "logging", NULL)) {
959                 logging = g_key_file_get_string(kf, "global", "logging", NULL);
960         }
961
962         if (g_key_file_has_group(kf, "log-custom")) {
963                 data = g_new0(struct log_file_config, 1);
964
965                 FETCH_SETTING(data, kf, "log-custom", "", nickchange);
966                 FETCH_SETTING(data, kf, "log-custom", "", logfilename);
967                 FETCH_SETTING(data, kf, "log-custom", "", topic);
968                 FETCH_SETTING(data, kf, "log-custom", "", notopic);
969                 FETCH_SETTING(data, kf, "log-custom", "", part);
970                 FETCH_SETTING(data, kf, "log-custom", "", join);
971                 FETCH_SETTING(data, kf, "log-custom", "", msg);
972                 FETCH_SETTING(data, kf, "log-custom", "", notice);
973                 FETCH_SETTING(data, kf, "log-custom", "", action);
974                 FETCH_SETTING(data, kf, "log-custom", "", kick);
975                 FETCH_SETTING(data, kf, "log-custom", "", quit);
976                 FETCH_SETTING(data, kf, "log-custom", "", mode);
977
978                 g_key_file_remove_group(kf, "log-custom", NULL);
979                 config->log_file = data;
980                 log_custom_load(data);
981         }
982
983         if (logging != NULL && !strcmp(logging, "custom")) {
984                 data = g_new0(struct log_file_config, 1);
985
986                 FETCH_SETTING(data, kf, "global", "", logfilename);
987                 FETCH_SETTING(data, kf, "global", "log-format-", nickchange);
988                 FETCH_SETTING(data, kf, "global", "log-format-", topic);
989                 FETCH_SETTING(data, kf, "global", "log-format-", notopic);
990                 FETCH_SETTING(data, kf, "global", "log-format-", part);
991                 FETCH_SETTING(data, kf, "global", "log-format-", join);
992                 FETCH_SETTING(data, kf, "global", "log-format-", msg);
993                 FETCH_SETTING(data, kf, "global", "log-format-", notice);
994                 FETCH_SETTING(data, kf, "global", "log-format-", action);
995                 FETCH_SETTING(data, kf, "global", "log-format-", kick);
996                 FETCH_SETTING(data, kf, "global", "log-format-", quit);
997                 FETCH_SETTING(data, kf, "global", "log-format-", mode);
998
999                 config->log_file = data;
1000                 log_custom_load(data);
1001         }
1002
1003         if (g_key_file_has_group(kf, "log-irssi") || 
1004                 (logging != NULL && !strcmp(logging, "irssi"))) {
1005                 data = g_new0(struct log_file_config, 1);
1006                 data->is_irssi = TRUE;
1007
1008                 data->join = "%h:%M -!- %n [%u] has joined %c";
1009                 data->part = "%h:%M -!- %n [%u] has left %c [%m]";
1010                 data->msg = "%h:%M < %n> %m";
1011                 data->notice = "%h:%M < %n> %m";
1012                 data->action = "%h:%M  * %n %m";
1013                 data->mode = "%h:%M -!- mode/%t [%p %c] by %n";
1014                 data->quit = "%h:%M -!- %n [%u] has quit [%m]";
1015                 data->kick = "%h:%M -!- %t has been kicked by %n [%m]";
1016                 data->topic = "%h:%M -!- %n has changed the topic to %t";
1017                 data->notopic = "%h:%M -!- %n has removed the topic";
1018                 data->nickchange = "%h:%M -!- %n is now known as %r";
1019
1020                 if (g_key_file_has_key(kf, "global", "logfile", NULL)) {
1021                         data->logfilename= g_key_file_get_string(kf, "global", "logfile", NULL);
1022                 } else if (g_key_file_has_key(kf, "global", "logdir", NULL)) {
1023                         data->logbasedir = g_key_file_get_string(kf, "global", "logdir", NULL);
1024                         data->logfilename = g_strdup_printf("%s/%%N/%%@", data->logbasedir);
1025                 } else if (g_key_file_has_key(kf, "log-irssi", "logfile", NULL)) {
1026                         data->logbasedir = g_key_file_get_string(kf, "log-irssi", "logfile", NULL);
1027                         data->logfilename = g_strdup_printf("%s/%%N/%%@", data->logbasedir);
1028                 } else {
1029                         data->logbasedir = g_build_filename(config->config_dir, 
1030                                                                                   "log_irssi", NULL);
1031
1032                         data->logbasedir_is_default = TRUE;
1033
1034                         data->logfilename = g_strdup_printf("%s/%%N/%%@", data->logbasedir);
1035                 }
1036                 g_key_file_remove_group(kf, "log-irssi", NULL);
1037
1038                 config->log_file = data;
1039                 log_custom_load(data);
1040         }
1041
1042         if (logging != NULL && 
1043                         strcmp(logging, "irssi") != 0 && 
1044                         strcmp(logging, "custom") != 0 &&
1045                         strcmp(logging, "none") != 0) {
1046                 log_global(LOG_WARNING, "Unknown log type `%s'", logging);
1047         }
1048
1049         g_free(logging);
1050 }
1051
1052 static void config_save_auto_away(struct auto_away_config *d, struct ctrlproxy_config *config)
1053 {
1054         GKeyFile *kf = config->keyfile;
1055         
1056         if (g_key_file_has_key(kf, "global", "auto-away-enable", NULL) ||
1057                 d->enabled)
1058                 g_key_file_set_boolean(kf, "global", "auto-away-enable", d->enabled);
1059
1060         if (d->message != NULL)
1061                 g_key_file_set_string(kf, "global", "auto-away-message", d->message);
1062
1063         if (d->nick != NULL)
1064                 g_key_file_set_string(kf, "global", "auto-away-nick", d->nick);
1065
1066         if (d->client_limit != -1)
1067                 g_key_file_set_integer(kf, "global", "auto-away-client-limit", d->client_limit);
1068
1069         if (d->max_idle_time != -1)
1070                 g_key_file_set_integer(kf, "global", "auto-away-time", d->max_idle_time);
1071 }
1072
1073 static void config_load_auto_away(struct auto_away_config *d, GKeyFile *kf)
1074 {
1075         if (g_key_file_has_group(kf, "auto-away")) {
1076                 d->enabled = TRUE;
1077                 d->message = g_key_file_get_string(kf, "auto-away", "message", NULL);
1078                 d->nick = g_key_file_get_string(kf, "auto-away", "nick", NULL);
1079                 if (g_key_file_has_key(kf, "auto-away", "client_limit", NULL)) {
1080                         d->client_limit = g_key_file_get_integer(kf, "auto-away", "client_limit", NULL);
1081                         if (g_key_file_has_key(kf, "auto-away", "only_noclient", NULL))
1082                                 log_global(LOG_WARNING, "auto-away: not using only_noclient because client_limit is set");
1083                 }
1084                 else if (g_key_file_has_key(kf, "auto-away", "only_noclient", NULL)) {
1085                         d->client_limit = g_key_file_get_boolean(kf, "auto-away", "only_noclient", NULL) ? 0 : -1;
1086                         log_global(LOG_WARNING, "auto-away: only_noclient is deprecated, please use client_limit instead");
1087                 }
1088                 else
1089                         d->client_limit = -1;
1090                 if (g_key_file_has_key(kf, "auto-away", "time", NULL))
1091                         d->max_idle_time = g_key_file_get_integer(kf, "auto-away", "time", NULL);
1092                 else
1093                         d->max_idle_time = -1;
1094
1095                 g_key_file_remove_group(kf, "auto-away", NULL);
1096         } else {
1097                 if (g_key_file_has_key(kf, "global", "auto-away-enable", NULL))
1098                         d->enabled = g_key_file_get_boolean(kf, "global", "auto-away-enable", NULL);
1099                 d->message = g_key_file_get_string(kf, "global", "auto-away-message", NULL);
1100                 d->nick = g_key_file_get_string(kf, "global", "auto-away-nick", NULL);
1101                 if (g_key_file_has_key(kf, "global", "auto-away-client-limit", NULL)) {
1102                         d->client_limit = g_key_file_get_integer(kf, "global", "auto-away-client-limit", NULL);
1103                 } else
1104                         d->client_limit = -1;
1105                 if (g_key_file_has_key(kf, "global", "auto-away-time", NULL))
1106                         d->max_idle_time = g_key_file_get_integer(kf, "global", "auto-away-time", NULL);
1107                 else
1108                         d->max_idle_time = -1;
1109         }
1110 }
1111
1112 struct ctrlproxy_config *init_configuration(void)
1113 {
1114         struct ctrlproxy_config *cfg;
1115         cfg = g_new0(struct ctrlproxy_config, 1);
1116
1117         return cfg;
1118 }
1119
1120 struct ctrlproxy_config *load_configuration(const char *dir) 
1121 {
1122         GKeyFile *kf;
1123         GError *error = NULL;
1124         struct ctrlproxy_config *cfg;
1125         char **keys;
1126         char *file;
1127         char **autoconnect_list;
1128         GList *gl;
1129         gsize size;
1130         int i;
1131         GList *channel_keys = NULL;
1132         char *keyfile_filename;
1133
1134         file = g_build_filename(dir, "config", NULL);
1135
1136         cfg = init_configuration();
1137         cfg->config_dir = g_strdup(dir);
1138         cfg->network_socket = g_build_filename(cfg->config_dir, "socket", NULL);
1139         cfg->admin_socket = g_build_filename(cfg->config_dir, "admin", NULL);
1140
1141         kf = cfg->keyfile = g_key_file_new();
1142
1143         if (!g_key_file_load_from_file(kf, file, G_KEY_FILE_KEEP_COMMENTS, &error)) {
1144                 log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", file, error->message);
1145                 g_key_file_free(kf);
1146                 g_free(file);
1147                 g_free(cfg);
1148                 return NULL;
1149         }
1150
1151         cfg->autosave = TRUE;
1152         if (g_key_file_has_key(kf, "global", "autosave", NULL) &&
1153                 !g_key_file_get_boolean(kf, "global", "autosave", NULL))
1154                 cfg->autosave = FALSE;
1155
1156         if (g_key_file_has_key(kf, "global", "keytab", NULL)) {
1157                 char *keytab;
1158                 keytab = g_key_file_get_string(kf, "global", "keytab", NULL);
1159 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1160                 gsskrb5_register_acceptor_identity(keytab);
1161 #endif
1162                 g_free(keytab);
1163         }
1164
1165         if (g_key_file_has_key(kf, "global", "max_who_age", NULL))
1166                 cfg->max_who_age = g_key_file_get_integer(kf, "global", "max_who_age", NULL);
1167
1168         cfg->replication = g_key_file_get_string(kf, "global", "replication", NULL);
1169         cfg->linestack_backend = g_key_file_get_string(kf, "global", "linestack", NULL);
1170
1171         if (g_key_file_has_key(kf, "global", "report-time", NULL)) {
1172                 char *setting = g_key_file_get_string(kf, "global", "report-time", NULL);
1173                 if (!g_strcasecmp(setting, "never") || !g_strcasecmp(setting, "false")) 
1174                         cfg->report_time = REPORT_TIME_NEVER;
1175                 else if (!g_strcasecmp(setting, "always"))
1176                         cfg->report_time = REPORT_TIME_ALWAYS;
1177                 else if  (!g_strcasecmp(setting, "replication") || 
1178                                   !g_strcasecmp(setting, "true"))
1179                         cfg->report_time = REPORT_TIME_REPLICATION;
1180                 else {
1181                         log_global(LOG_WARNING, "Unknown value `%s' for report-time in configuration file", setting);
1182                 }
1183                 g_free(setting);
1184         }
1185
1186         cfg->report_time_offset = 0;
1187         if (g_key_file_has_key(kf, "global", "report-time-offset", NULL)) {
1188                 cfg->report_time_offset = g_key_file_get_integer(kf, "global", "report-time-offset", NULL);
1189         }
1190
1191     if (g_key_file_has_key(kf, "global", "motd-file", NULL))
1192                 cfg->motd_file = g_key_file_get_string(kf, "global", "motd-file", NULL);
1193     else 
1194             cfg->motd_file = g_build_filename(SHAREDIR, "motd", NULL);
1195
1196     if (g_key_file_has_key(kf, "client", "charset", NULL)) {
1197                 cfg->client_charset = g_key_file_get_string(kf, "client", "charset", NULL);
1198                 g_key_file_remove_key(cfg->keyfile, "client", "charset", NULL); /* deprecated */
1199         } else if (g_key_file_has_key(kf, "global", "client-charset", NULL)) {
1200                 cfg->client_charset = g_key_file_get_string(kf, "global", "client-charset", NULL);
1201                 g_key_file_remove_key(cfg->keyfile, "global", "client-charset", NULL); /* deprecated */
1202         } else if (g_key_file_has_key(kf, "global", "default-client-charset", NULL)) {
1203                 cfg->client_charset = g_key_file_get_string(kf, "global", "default-client-charset", NULL);
1204         } else {
1205             cfg->client_charset = NULL;
1206         }
1207
1208     if (g_key_file_has_key(kf, "global", "learn-nickserv", NULL))
1209                 cfg->learn_nickserv = g_key_file_get_boolean(kf, "global", "learn-nickserv", NULL);
1210     else 
1211             cfg->learn_nickserv = TRUE;
1212
1213     if (g_key_file_has_key(kf, "global", "learn-network-name", NULL))
1214                 cfg->learn_network_name = g_key_file_get_boolean(kf, "global", "learn-network-name", NULL);
1215     else 
1216             cfg->learn_network_name = TRUE;
1217
1218         if (g_key_file_has_key(kf, "global", "create-implicit", NULL))
1219                 cfg->create_implicit = g_key_file_get_boolean(kf, "global", "create-implicit", NULL);
1220         else
1221                 cfg->create_implicit = TRUE;
1222
1223         if (!g_file_test(cfg->motd_file, G_FILE_TEST_EXISTS))
1224                 log_global(LOG_ERROR, "Can't open MOTD file '%s' for reading", cfg->motd_file);
1225
1226     if (g_key_file_has_key(kf, "admin", "without_privmsg", NULL)) {
1227                 if (g_key_file_get_boolean(kf, "admin", "without_privmsg", NULL)) {
1228                         cfg->admin_user = NULL;
1229                 } else {
1230                         cfg->admin_user = g_strdup("ctrlproxy");
1231                 }
1232                 g_key_file_remove_key(kf, "admin", "without_privmsg", NULL);
1233         }
1234
1235         if (g_key_file_has_key(kf, "global", "admin-user", NULL)) {
1236                 cfg->admin_user = g_key_file_get_string(kf, "global", "admin-user", NULL);
1237         }
1238
1239         cfg->admin_log = TRUE;
1240     if (g_key_file_has_key(kf, "admin", "log", NULL) && !g_key_file_get_boolean(kf, "admin", "log", NULL))
1241         cfg->admin_log = FALSE;
1242         g_key_file_remove_key(kf, "admin", "log", NULL);
1243     if (g_key_file_has_key(kf, "global", "admin-log", NULL) && !g_key_file_get_boolean(kf, "global", "admin-log", NULL))
1244         cfg->admin_log = FALSE;
1245         g_key_file_remove_group(kf, "admin", NULL);
1246
1247         for (gl = cfg->networks; gl; gl = gl->next) {
1248                 struct network_config *nc = gl->data;
1249
1250                 nc->autoconnect = FALSE;
1251         }
1252
1253         config_load_listeners(cfg);
1254         config_load_listeners_socks(cfg);
1255         config_load_log(cfg);
1256         config_load_auto_away(&cfg->auto_away, cfg->keyfile);
1257
1258         keyfile_filename = g_build_filename(cfg->config_dir, "keys", 
1259                                                                           NULL);
1260
1261         if (g_file_test(keyfile_filename, G_FILE_TEST_EXISTS)) {
1262                 if (!keyfile_read_file(keyfile_filename, ';', &channel_keys)) {
1263                         log_global(LOG_WARNING, "Unable to read keys file");
1264                 }
1265         }
1266
1267         g_free(keyfile_filename);
1268
1269         config_load_networks(cfg, channel_keys);
1270
1271         /* Check for unknown parameters */
1272         keys = g_key_file_get_keys(kf, "global", NULL, NULL);
1273         for (i = 0; keys[i] != NULL; i++) {
1274                 if (!config_known_key(keys[i])) 
1275                         log_global(LOG_WARNING, "Unknown setting `%s' in configuration file", keys[i]);
1276         }
1277         g_strfreev(keys);
1278
1279         size = 0;
1280         autoconnect_list = g_key_file_get_string_list(kf, "global", "autoconnect", &size, NULL);
1281                 
1282         for (i = 0; i < size; i++) {
1283                 struct network_config *nc = find_create_network_config(cfg, autoconnect_list[i]);
1284
1285                 g_assert(nc);
1286                 nc->autoconnect = TRUE;
1287         }
1288
1289         g_strfreev(autoconnect_list);
1290
1291         g_free(file);
1292
1293         return cfg;
1294 }
1295
1296 struct network_config *network_config_init(struct ctrlproxy_config *cfg) 
1297 {
1298         struct network_config *s = g_new0(struct network_config, 1);
1299
1300         s->autoconnect = FALSE;
1301         s->nick = g_strdup(g_get_user_name());
1302         s->username = g_strdup(g_get_user_name());
1303         g_assert(s->username != NULL && strlen(s->username) > 0);
1304         s->fullname = g_strdup(g_get_real_name());
1305         if (s->fullname == NULL || 
1306                 strlen(s->fullname) == 0) {
1307                 g_free(s->fullname);
1308                 s->fullname = g_strdup(s->username);
1309         }
1310         s->reconnect_interval = -1;
1311
1312         if (cfg) 
1313                 cfg->networks = g_list_append(cfg->networks, s);
1314         return s;
1315 }
1316
1317 void free_config(struct ctrlproxy_config *cfg)
1318 {
1319         while (cfg->networks) {
1320                 struct network_config *nc = cfg->networks->data;
1321                 g_free(nc->name);
1322                 g_free(nc->nick);
1323                 g_free(nc->fullname);
1324                 g_free(nc->username);
1325                 g_free(nc->password);
1326                 g_strfreev(nc->autocmd);
1327                 while (nc->channels) {
1328                         struct channel_config *cc = nc->channels->data;
1329                         g_free(cc->name);
1330                         g_free(cc->key);
1331                         nc->channels = g_list_remove(nc->channels, cc); 
1332                         g_free(cc);
1333                 }
1334                 switch (nc->type) {
1335                 case NETWORK_TCP: 
1336                         while (nc->type_settings.tcp.servers) {
1337                                 struct tcp_server_config *tc = nc->type_settings.tcp.servers->data;
1338                                 g_free(tc->host);
1339                                 g_free(tc->port);
1340                                 g_free(tc->bind_address);
1341                                 g_free(tc->password);
1342                                 nc->type_settings.tcp.servers = g_list_remove(nc->type_settings.tcp.servers, tc);
1343                                 g_free(tc);
1344                         }
1345                         g_free(nc->type_settings.tcp.default_bind_address);
1346                         break;
1347                 case NETWORK_VIRTUAL:
1348                         g_free(nc->type_settings.virtual_type);
1349                         break;
1350                 case NETWORK_PROGRAM:
1351                         g_free(nc->type_settings.program_location);
1352                         break;
1353                 case NETWORK_IOCHANNEL:
1354                         /* Nothing to free */
1355                         break;
1356                 }
1357                 cfg->networks = g_list_remove(cfg->networks, nc);
1358                 if (nc->keyfile) g_key_file_free(nc->keyfile);
1359                 g_free(nc);
1360         }
1361         g_free(cfg->config_dir);
1362         g_free(cfg->network_socket);
1363         g_free(cfg->admin_socket);
1364         g_free(cfg->replication);
1365         g_free(cfg->linestack_backend);
1366         g_free(cfg->motd_file);
1367         g_free(cfg->admin_user);
1368         g_key_file_free(cfg->keyfile);
1369         g_free(cfg);
1370 }
1371
1372 gboolean create_configuration(const char *config_dir)
1373 {
1374         struct global *global;
1375         char port[250];
1376         struct listener_config *l;
1377         char *pass;
1378
1379         if (g_file_test(config_dir, G_FILE_TEST_IS_DIR)) {
1380                 fprintf(stderr, "%s already exists\n", config_dir);
1381                 return FALSE;
1382         }
1383
1384         if (g_mkdir(config_dir, 0700) != 0) {
1385                 fprintf(stderr, "Can't create config directory '%s': %s\n", config_dir, strerror(errno));
1386                 return FALSE;
1387         }
1388
1389         global = load_global(DEFAULT_CONFIG_DIR);       
1390         if (global == NULL) { 
1391                 fprintf(stderr, "Unable to load default configuration '%s'\n", DEFAULT_CONFIG_DIR);     
1392                 return FALSE;
1393         }
1394         global->config->config_dir = g_strdup(config_dir);
1395         save_configuration(global->config, config_dir);
1396
1397         snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
1398         printf("Please specify port the administration interface should listen on.\n"
1399                    "Prepend with a colon to listen on a specific address.\n"
1400                    "Example: localhost:6668\n\nPort [%s]: ", port); fflush(stdout);
1401         if (!fgets(port, sizeof(port), stdin))
1402                 snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
1403
1404         if (port[strlen(port)-1] == '\n')
1405                 port[strlen(port)-1] = '\0';
1406
1407         if (strlen(port) == 0) 
1408                 snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
1409
1410         l = g_new0(struct listener_config, 1);
1411         pass = getpass("Please specify a password for the administration interface: "); 
1412         l->port = port;
1413         l->is_default = TRUE;
1414         if (!strcmp(pass, "")) {
1415                 fprintf(stderr, "Warning: no password specified. Authentication disabled!\n");
1416         } else {
1417                 l->password = g_strdup(pass);
1418         }
1419
1420         global->config->default_listener = l;
1421
1422         global->config->listeners = g_list_append(global->config->listeners, l);
1423
1424         save_configuration(global->config, config_dir);
1425
1426         return TRUE;
1427 }