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