2 ctrlproxy: A modular IRC proxy
3 (c) 2002-2007 Jelmer Vernooij <jelmer@nl.linux.org>
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.
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.
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.
21 #include "internals.h"
24 #ifdef HAVE_SYS_SOCKET_H
25 #include <sys/socket.h>
29 #include <sys/socket.h>
30 #include <glib/gstdio.h>
32 #define DEFAULT_ADMIN_PORT 6680
33 #define DEFAULT_SOCKS_PORT 1080
35 gboolean g_key_file_save_to_file(GKeyFile *kf, const gchar *file, GError **error)
38 char *data = g_key_file_to_data(kf, &length, error);
44 gio = g_io_channel_new_file(file, "w+", error);
50 g_io_channel_write_chars(gio, data, length, &nr, error);
54 g_io_channel_unref(gio);
59 static void config_save_tcp_servers(struct network_config *n, GKeyFile *kf)
63 gchar **values = g_new0(gchar *, g_list_length(n->type_settings.tcp_servers)+1);
65 for (gl = n->type_settings.tcp_servers; gl; gl = gl->next) {
66 struct tcp_server_config *ts = gl->data;
67 char *name = g_strdup_printf("%s:%s", ts->host, ts->port);
71 if (g_key_file_has_key(kf, name, "ssl", NULL) || ts->ssl)
72 g_key_file_set_boolean(kf, name, "ssl", ts->ssl);
75 g_key_file_set_string(kf, name, "password", ts->password);
77 g_key_file_remove_key(kf, name, "password", NULL);
79 if (ts->bind_address) {
82 tmp = g_strdup_printf("%s:%s",
86 tmp = g_strdup(ts->bind_address);
88 g_key_file_set_string(kf, name, "bind", tmp);
92 g_key_file_remove_key(kf, name, "bind", NULL);
97 g_key_file_set_string_list(kf, "global", "servers", (const gchar **)values, i);
102 static void config_save_network(const char *dir, struct network_config *n)
109 n->keyfile = g_key_file_new();
114 g_key_file_set_string(kf, "global", "fullname", n->fullname);
115 g_key_file_set_string(kf, "global", "nick", n->nick);
116 g_key_file_set_string(kf, "global", "username", n->username);
118 g_key_file_set_integer(kf, "global", "queue-speed", n->queue_speed);
119 g_key_file_set_integer(kf, "global", "reconnect-interval", n->reconnect_interval);
122 case NETWORK_VIRTUAL:
123 g_key_file_set_string(kf, "global", "virtual", n->type_settings.virtual_type);
125 case NETWORK_PROGRAM:
126 g_key_file_set_string(kf, "global", "program", n->type_settings.program_location);
129 config_save_tcp_servers(n, kf);
134 for (gl = n->channels; gl; gl = gl->next) {
135 struct channel_config *c = gl->data;
138 g_key_file_set_string(kf, c->name, "key", c->key);
140 g_key_file_remove_key(kf, c->name, "key", NULL);
142 g_key_file_set_boolean(kf, c->name, "autojoin", c->autojoin);
145 fn = g_build_filename(dir, n->name, NULL);
146 g_key_file_save_to_file(kf, fn, NULL);
150 static void config_save_listeners(struct ctrlproxy_config *cfg, const char *path)
155 GError *error = NULL;
156 gboolean empty = TRUE;
157 char *default_password;
159 default_password = g_key_file_get_string(cfg->keyfile, "listener", "password", NULL);
161 if (cfg->auto_listener) {
162 g_key_file_set_boolean(cfg->keyfile, "listener", "auto", cfg->auto_listener);
163 g_key_file_set_integer(cfg->keyfile, "listener", "autoport", cfg->listener_autoport);
166 filename = g_build_filename(path, "listener", NULL);
168 kf = g_key_file_new();
170 for (gl = cfg->listeners; gl; gl = gl->next) {
171 struct listener_config *l = gl->data;
174 g_key_file_set_string(cfg->keyfile, "global", "port", l->port);
175 if (l->address != NULL)
176 g_key_file_set_string(cfg->keyfile, "global", "bind", l->address);
177 if (l->password != NULL)
178 g_key_file_set_string(cfg->keyfile, "global", "password", l->password);
180 if (g_key_file_has_key(cfg->keyfile, "global", "ssl", NULL) || l->ssl)
181 g_key_file_set_boolean(cfg->keyfile, "global", "ssl", l->ssl);
183 if (l->network != NULL)
184 g_key_file_set_string(cfg->keyfile, "global", "default-network",
190 tmp = g_strdup(l->port);
192 tmp = g_strdup_printf("%s:%s", l->address, l->port);
194 if (l->password != NULL &&
195 !(default_password != NULL && strcmp(l->password, default_password) == 0))
196 g_key_file_set_string(kf, tmp, "password", l->password);
198 if (l->network != NULL) {
199 g_key_file_set_string(kf, tmp, "network", l->network);
202 g_key_file_set_boolean(kf, tmp, "ssl", l->ssl);
211 if (!g_key_file_save_to_file(kf, filename, &error)) {
212 log_global(LOG_WARNING, "Unable to save to \"%s\": %s", filename, error->message);
219 static void config_save_networks(const char *config_dir, GList *networks)
221 char *networksdir = g_build_filename(config_dir, "networks", NULL);
224 if (!g_file_test(networksdir, G_FILE_TEST_IS_DIR)) {
225 if (g_mkdir(networksdir, 0700) != 0) {
226 log_global(LOG_ERROR, "Can't create networks directory '%s': %s", networksdir, strerror(errno));
231 for (gl = networks; gl; gl = gl->next) {
232 struct network_config *n = gl->data;
233 config_save_network(networksdir, n);
239 void save_configuration(struct ctrlproxy_config *cfg, const char *configuration_dir)
245 if (!g_file_test(configuration_dir, G_FILE_TEST_IS_DIR)) {
246 if (g_mkdir(configuration_dir, 0700) != 0) {
247 log_global(LOG_ERROR, "Unable to open configuration directory '%s'\n", configuration_dir);
253 cfg->keyfile = g_key_file_new();
255 g_key_file_set_boolean(cfg->keyfile, "global", "autosave", cfg->autosave);
256 if (cfg->admin_user != NULL)
257 g_key_file_set_string(cfg->keyfile, "global", "admin-user", cfg->admin_user);
259 if (g_key_file_has_key(cfg->keyfile, "global", "admin-log", NULL) ||
261 g_key_file_set_boolean(cfg->keyfile, "global", "admin-log", cfg->admin_log);
263 if (g_key_file_has_key(cfg->keyfile, "global", "max_who_age", NULL) ||
264 cfg->max_who_age != 0)
265 g_key_file_set_integer(cfg->keyfile, "global", "max_who_age", cfg->max_who_age);
267 if (g_key_file_has_key(cfg->keyfile, "global", "learn-nickserv", NULL) ||
268 !cfg->learn_nickserv)
269 g_key_file_set_boolean(cfg->keyfile, "global", "learn-nickserv", cfg->learn_nickserv);
271 if (g_key_file_has_key(cfg->keyfile, "global", "learn-network-name", NULL) ||
272 !cfg->learn_network_name)
273 g_key_file_set_boolean(cfg->keyfile, "global", "learn-network-name", cfg->learn_network_name);
275 if (cfg->client_charset != NULL)
276 g_key_file_set_string(cfg->keyfile, "global", "client-charset", cfg->client_charset);
277 if (cfg->replication)
278 g_key_file_set_string(cfg->keyfile, "global", "replication", cfg->replication);
279 if (cfg->linestack_backend)
280 g_key_file_set_string(cfg->keyfile, "global", "linestack", cfg->linestack_backend);
281 if (cfg->motd_file != NULL)
282 g_key_file_set_string(cfg->keyfile, "global", "motd-file", cfg->motd_file);
284 g_key_file_set_boolean(cfg->keyfile, "global", "report-time", cfg->report_time);
286 config_save_networks(configuration_dir, cfg->networks);
288 config_save_listeners(cfg, configuration_dir);
291 list = g_new0(char *, g_list_length(cfg->networks)+1);
292 for (gl = cfg->networks; gl; gl = gl->next) {
293 struct network_config *nc = gl->data;
295 if (nc->autoconnect) {
302 g_key_file_set_string_list(cfg->keyfile, "global", "autoconnect", (const gchar **)list, i);
306 fn = g_build_filename(configuration_dir, "config", NULL);
307 g_key_file_save_to_file(cfg->keyfile, fn, NULL);
311 static void config_load_channel(struct network_config *n, GKeyFile *kf, const char *name)
313 struct channel_config *ch = g_new0(struct channel_config, 1);
315 ch->name = g_strdup(name);
316 if (g_key_file_has_key(kf, name, "key", NULL))
317 ch->key = g_key_file_get_string(kf, name, "key", NULL);
319 if (g_key_file_has_key(kf, name, "autojoin", NULL))
320 ch->autojoin = g_key_file_get_boolean(kf, name, "autojoin", NULL);
322 n->channels = g_list_append(n->channels, ch);
325 static void config_load_servers(struct network_config *n)
331 servers = g_key_file_get_string_list(n->keyfile, "global", "servers", &size, NULL);
336 for (i = 0; i < size; i++) {
338 struct tcp_server_config *s = g_new0(struct tcp_server_config, 1);
340 s->password = g_key_file_get_string(n->keyfile, servers[i], "password", NULL);
341 if (g_key_file_has_key(n->keyfile, servers[i], "ssl", NULL))
342 s->ssl = g_key_file_get_boolean(n->keyfile, servers[i], "ssl", NULL);
344 tmp = strrchr(servers[i], ':');
351 s->host = servers[i];
352 s->port = g_strdup(tmp != NULL?tmp:DEFAULT_IRC_PORT);
353 s->bind_address = g_key_file_get_string(n->keyfile, servers[i], "bind", NULL);
354 if (s->bind_address && (tmp = strchr(s->bind_address, ':'))) {
356 s->bind_port = tmp+1;
359 n->type_settings.tcp_servers = g_list_append(n->type_settings.tcp_servers, s);
365 static struct network_config *config_load_network(struct ctrlproxy_config *cfg, const char *dirname, const char *name)
368 struct network_config *n;
372 GError *error = NULL;
375 kf = g_key_file_new();
377 filename = g_build_filename(dirname, name, NULL);
379 if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {
380 log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", filename, error->message);
385 n = network_config_init(cfg);
390 if (g_key_file_has_key(kf, "global", "fullname", NULL)) {
392 n->fullname = g_key_file_get_string(kf, "global", "fullname", NULL);
393 if (!strcmp(n->fullname, "") || n->fullname[0] == ' ')
394 log_global(LOG_WARNING, "Invalid fullname `%s' set for network `%s'", n->fullname, n->name);
397 if (g_key_file_has_key(kf, "global", "nick", NULL)) {
399 n->nick = g_key_file_get_string(kf, "global", "nick", NULL);
400 if (!strcmp(n->nick, "") || n->nick[0] == ' ')
401 log_global(LOG_WARNING, "Invalid nick name `%s' set for `%s'", n->nick, n->name);
404 if (g_key_file_has_key(kf, "global", "reconnect-interval", NULL)) {
405 n->reconnect_interval = g_key_file_get_integer(kf, "global", "reconnect-interval", NULL);
408 if (g_key_file_has_key(kf, "global", "queue-speed", NULL)) {
409 n->queue_speed = g_key_file_get_integer(kf, "global", "queue-speed", NULL);
412 if (g_key_file_has_key(kf, "global", "username", NULL)) {
414 n->username = g_key_file_get_string(kf, "global", "username", NULL);
415 if (!strcmp(n->username, "") || n->username[0] == ' ')
416 log_global(LOG_WARNING, "Invalid username `%s' set for network `%s'", n->username, n->name);
419 if (g_key_file_has_key(kf, "global", "ignore_first_nick", NULL)) {
420 n->ignore_first_nick = g_key_file_get_boolean(kf, "global", "ignore_first_nick", NULL);
423 if (g_key_file_has_key(kf, "global", "password", NULL)) {
425 n->password = g_key_file_get_string(kf, "global", "password", NULL);
428 n->name = g_strdup(name);
430 if (g_key_file_has_key(kf, "global", "program", NULL))
431 n->type = NETWORK_PROGRAM;
432 else if (g_key_file_has_key(kf, "global", "virtual", NULL))
433 n->type = NETWORK_VIRTUAL;
435 n->type = NETWORK_TCP;
439 config_load_servers(n);
441 case NETWORK_PROGRAM:
442 n->type_settings.program_location = g_key_file_get_string(kf, "global", "program", NULL);
444 case NETWORK_VIRTUAL:
445 n->type_settings.virtual_type = g_key_file_get_string(kf, "global", "virtual", NULL);
447 case NETWORK_IOCHANNEL:
452 groups = g_key_file_get_groups(kf, &size);
453 for (i = 0; i < size; i++) {
454 if (!g_ascii_isalpha(groups[i][0]))
455 config_load_channel(n, kf, groups[i]);
463 static struct network_config *find_create_network_config(struct ctrlproxy_config *cfg, const char *name)
466 struct network_config *nc;
467 struct tcp_server_config *tc;
469 for (gl = cfg->networks; gl; gl = gl->next) {
473 if (g_strcasecmp(nc->name, name) == 0)
476 if (nc->type != NETWORK_TCP)
479 for (gl1 = nc->type_settings.tcp_servers; gl1; gl1 = gl1->next) {
481 struct tcp_server_config *sc = gl1->data;
483 if (g_strcasecmp(sc->host, name) == 0)
486 if (g_strncasecmp(sc->host, name, strlen(sc->host)) != 0)
489 tmp = g_strdup_printf("%s:%s", sc->host, sc->port);
491 if (g_strcasecmp(tmp, name) == 0)
498 nc = network_config_init(cfg);
499 nc->name = g_strdup(name);
500 nc->autoconnect = FALSE;
501 nc->reconnect_interval = DEFAULT_RECONNECT_INTERVAL;
502 nc->type = NETWORK_TCP;
503 tc = g_new0(struct tcp_server_config, 1);
504 tc->host = g_strdup(name);
505 if (strchr(tc->host, ':')) {
506 tc->port = tc->host+1;
509 tc->port = g_strdup(DEFAULT_IRC_PORT);
512 nc->type_settings.tcp_servers = g_list_append(nc->type_settings.tcp_servers, tc);
514 cfg->networks = g_list_append(cfg->networks, nc);
519 static void config_load_listeners_socks(struct ctrlproxy_config *cfg)
523 GKeyFile *kf = cfg->keyfile;
524 struct listener_config *l;
526 allows = g_key_file_get_string_list(kf, "socks", "allow", &size, NULL);
531 g_key_file_remove_key(kf, "socks", "allow", NULL);
533 l = g_new0(struct listener_config, 1);
535 if (g_key_file_has_key(kf, "socks", "port", NULL))
536 l->port = g_key_file_get_string(kf, "socks", "port", NULL);
538 l->port = g_strdup_printf("%d", DEFAULT_SOCKS_PORT);
540 /* We can use the socks listener as default listener, if there was
541 * no default listener specified */
542 if (cfg->listeners == NULL ||
543 !((struct listener_config *)cfg->listeners->data)->is_default)
544 l->is_default = TRUE;
546 g_key_file_remove_key(kf, "socks", "port", NULL);
548 for (i = 0; i < size; i++) {
549 struct allow_rule *r = g_new0(struct allow_rule, 1);
550 char **parts = g_strsplit(allows[i], ":", 2);
552 r->username = parts[0];
553 r->password = parts[1];
556 l->allow_rules = g_list_append(l->allow_rules, r);
561 cfg->listeners = g_list_append(cfg->listeners, l);
564 static void config_load_listeners(struct ctrlproxy_config *cfg)
566 char *filename = g_build_filename(cfg->config_dir, "listener", NULL);
571 char *default_password;
572 GError *error = NULL;
574 default_password = g_key_file_get_string(cfg->keyfile, "listener", "password", NULL);
575 if (g_key_file_has_key(cfg->keyfile, "listener", "auto", NULL))
576 cfg->auto_listener = g_key_file_get_boolean(cfg->keyfile, "listener", "auto", NULL);
578 if (g_key_file_has_key(cfg->keyfile, "listener", "autoport", NULL))
579 cfg->listener_autoport = g_key_file_get_integer(cfg->keyfile, "listener", "autoport", NULL);
581 if (g_key_file_has_key(cfg->keyfile, "global", "port", NULL)) {
582 struct listener_config *l = g_new0(struct listener_config, 1);
583 l->port = g_key_file_get_string(cfg->keyfile, "global", "port", NULL);
584 l->password = g_key_file_get_string(cfg->keyfile, "global", "password", NULL);
585 l->address = g_key_file_get_string(cfg->keyfile, "global", "bind", NULL);
586 l->ssl = g_key_file_has_key(cfg->keyfile, "global", "ssl", NULL) &&
587 g_key_file_get_boolean(cfg->keyfile, "global", "ssl", NULL);
588 l->is_default = TRUE;
590 l->network = g_key_file_get_string(cfg->keyfile, "global", "default-network", NULL);
592 cfg->listeners = g_list_append(cfg->listeners, l);
595 kf = g_key_file_new();
597 if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, &error)) {
598 if (error->code != G_FILE_ERROR_NOENT)
599 log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", filename, error->message);
604 groups = g_key_file_get_groups(kf, &size);
606 for (i = 0; i < size; i++)
608 struct listener_config *l;
611 l = g_new0(struct listener_config, 1);
613 tmp = g_strdup(groups[i]);
614 l->port = strrchr(tmp, ':');
615 if (l->port != NULL) {
624 l->password = g_key_file_get_string(kf, groups[i], "password", NULL);
625 if (l->password == NULL)
626 l->password = default_password;
628 if (g_key_file_has_key(kf, groups[i], "ssl", NULL))
629 l->ssl = g_key_file_get_boolean(kf, groups[i], "ssl", NULL);
633 l->ssl_credentials = ssl_create_server_credentials(cfg, kf, groups[i]);
636 if (g_key_file_has_key(kf, groups[i], "network", NULL))
637 l->network = g_key_file_get_string(kf, groups[i], "network", NULL);
639 cfg->listeners = g_list_append(cfg->listeners, l);
646 static void config_load_networks(struct ctrlproxy_config *cfg)
648 char *networksdir = g_build_filename(cfg->config_dir, "networks", NULL);
652 dir = g_dir_open(networksdir, 0, NULL);
656 while ((name = g_dir_read_name(dir))) {
657 if (name[0] == '.' || name[strlen(name)-1] == '~')
659 config_load_network(cfg, networksdir, name);
667 #define FETCH_SETTING(data, kf, section, prefix, name) (data)->name = g_key_file_get_string((kf), (section), prefix __STRING(name), NULL)
669 static void config_load_log(struct ctrlproxy_config *config)
671 GKeyFile *kf = config->keyfile;
672 struct log_file_config *data;
674 char *logging = NULL;
676 if (g_key_file_get_string(kf, "global", "logging", NULL) != NULL) {
677 logging = g_key_file_get_string(kf, "global", "logging", NULL);
680 if (g_key_file_has_group(kf, "log-custom")) {
681 data = g_new0(struct log_file_config, 1);
683 FETCH_SETTING(data, kf, "log-custom", "", nickchange);
684 FETCH_SETTING(data, kf, "log-custom", "", logfilename);
685 FETCH_SETTING(data, kf, "log-custom", "", topic);
686 FETCH_SETTING(data, kf, "log-custom", "", notopic);
687 FETCH_SETTING(data, kf, "log-custom", "", part);
688 FETCH_SETTING(data, kf, "log-custom", "", join);
689 FETCH_SETTING(data, kf, "log-custom", "", msg);
690 FETCH_SETTING(data, kf, "log-custom", "", notice);
691 FETCH_SETTING(data, kf, "log-custom", "", action);
692 FETCH_SETTING(data, kf, "log-custom", "", kick);
693 FETCH_SETTING(data, kf, "log-custom", "", quit);
694 FETCH_SETTING(data, kf, "log-custom", "", mode);
696 log_custom_load(data);
699 if (logging != NULL && !strcmp(logging, "custom")) {
700 data = g_new0(struct log_file_config, 1);
702 FETCH_SETTING(data, kf, "global", "", logfilename);
703 FETCH_SETTING(data, kf, "global", "log-format-", nickchange);
704 FETCH_SETTING(data, kf, "global", "log-format-", topic);
705 FETCH_SETTING(data, kf, "global", "log-format-", notopic);
706 FETCH_SETTING(data, kf, "global", "log-format-", part);
707 FETCH_SETTING(data, kf, "global", "log-format-", join);
708 FETCH_SETTING(data, kf, "global", "log-format-", msg);
709 FETCH_SETTING(data, kf, "global", "log-format-", notice);
710 FETCH_SETTING(data, kf, "global", "log-format-", action);
711 FETCH_SETTING(data, kf, "global", "log-format-", kick);
712 FETCH_SETTING(data, kf, "global", "log-format-", quit);
713 FETCH_SETTING(data, kf, "global", "log-format-", mode);
715 log_custom_load(data);
718 if (g_key_file_has_group(kf, "log-irssi") ||
719 (logging != NULL && !strcmp(logging, "irssi"))) {
720 data = g_new0(struct log_file_config, 1);
722 data->join = "%h:%M -!- %n [%u] has joined %c";
723 data->part = "%h:%M -!- %n [%u] has left %c [%m]";
724 data->msg = "%h:%M < %n> %m";
725 data->notice = "%h:%M < %n> %m";
726 data->action = "%h:%M * %n %m";
727 data->mode = "%h:%M -!- mode/%t [%p %c] by %n";
728 data->quit = "%h:%M -!- %n [%u] has quit [%m]";
729 data->kick = "%h:%M -!- %t has been kicked by %n [%m]";
730 data->topic = "%h:%M -!- %n has changed the topic to %t";
731 data->notopic = "%h:%M -!- %n has removed the topic";
732 data->nickchange = "%h:%M -!- %n is now known as %r";
734 if (g_key_file_has_key(kf, "log-irssi", "logfile", NULL)) {
735 logbasedir = g_key_file_get_string(kf, "log-irssi", "logfile", NULL);
736 } else if (g_key_file_has_key(kf, "global", "logfile", NULL)) {
737 logbasedir = g_key_file_get_string(kf, "global", "logfile", NULL);
739 logbasedir = g_build_filename(config->config_dir,
743 data->logfilename = g_strdup_printf("%s/%%N/%%@", logbasedir);
747 log_custom_load(data);
750 if (logging != NULL &&
751 strcmp(logging, "irssi") != 0 &&
752 strcmp(logging, "custom") != 0 &&
753 strcmp(logging, "none") != 0) {
754 log_global(LOG_WARNING, "Unknown log type `%s'", logging);
760 static void config_load_auto_away(struct ctrlproxy_config *config)
762 struct auto_away_config *d;
763 GKeyFile *kf = config->keyfile;
765 if (g_key_file_has_group(kf, "auto-away")) {
766 d = g_new0(struct auto_away_config, 1);
768 d->message = g_key_file_get_string(kf, "auto-away", "message", NULL);
769 d->nick = g_key_file_get_string(kf, "auto-away", "nick", NULL);
770 if (g_key_file_has_key(kf, "auto-away", "client_limit", NULL)) {
771 d->client_limit = g_key_file_get_integer(kf, "auto-away", "client_limit", NULL);
772 if (g_key_file_has_key(kf, "auto-away", "only_noclient", NULL))
773 log_global(LOG_WARNING, "auto-away: not using only_noclient because client_limit is set");
775 else if (g_key_file_has_key(kf, "auto-away", "only_noclient", NULL)) {
776 d->client_limit = g_key_file_get_boolean(kf, "auto-away", "only_noclient", NULL) ? 0 : -1;
777 log_global(LOG_WARNING, "auto-away: only_noclient is deprecated, please use client_limit instead");
780 d->client_limit = -1;
781 if (g_key_file_has_key(kf, "auto-away", "time", NULL))
782 d->max_idle_time = g_key_file_get_integer(kf, "auto-away", "time", NULL);
784 d->max_idle_time = AUTO_AWAY_DEFAULT_TIME;
785 } else if (g_key_file_has_key(kf, "global", "auto-away-enable", NULL) &&
786 g_key_file_get_boolean(kf, "global", "auto-away-enable", NULL)) {
787 d = g_new0(struct auto_away_config, 1);
789 d->message = g_key_file_get_string(kf, "global", "auto-away-message", NULL);
790 d->nick = g_key_file_get_string(kf, "global", "auto-away-nick", NULL);
791 if (g_key_file_has_key(kf, "global", "auto-away-client-limit", NULL)) {
792 d->client_limit = g_key_file_get_integer(kf, "global", "auto-away-client-limit", NULL);
795 d->client_limit = -1;
796 if (g_key_file_has_key(kf, "global", "auto-away-time", NULL))
797 d->max_idle_time = g_key_file_get_integer(kf, "global", "auto-away-time", NULL);
799 d->max_idle_time = AUTO_AWAY_DEFAULT_TIME;
804 config->auto_away = d;
807 struct ctrlproxy_config *init_configuration(void)
809 struct ctrlproxy_config *cfg;
810 cfg = g_new0(struct ctrlproxy_config, 1);
815 struct ctrlproxy_config *load_configuration(const char *dir)
818 GError *error = NULL;
819 struct ctrlproxy_config *cfg;
821 char **autoconnect_list;
826 file = g_build_filename(dir, "config", NULL);
828 cfg = init_configuration();
829 cfg->config_dir = g_strdup(dir);
830 cfg->network_socket = g_build_filename(cfg->config_dir, "socket", NULL);
831 cfg->admin_socket = g_build_filename(cfg->config_dir, "admin", NULL);
833 kf = cfg->keyfile = g_key_file_new();
835 if (!g_key_file_load_from_file(kf, file, G_KEY_FILE_KEEP_COMMENTS, &error)) {
836 log_global(LOG_ERROR, "Can't parse configuration file '%s': %s", file, error->message);
843 cfg->autosave = TRUE;
844 if (g_key_file_has_key(kf, "global", "autosave", NULL) &&
845 !g_key_file_get_boolean(kf, "global", "autosave", NULL))
846 cfg->autosave = FALSE;
848 if (g_key_file_has_key(kf, "global", "max_who_age", NULL))
849 cfg->max_who_age = g_key_file_get_integer(kf, "global", "max_who_age", NULL);
851 cfg->replication = g_key_file_get_string(kf, "global", "replication", NULL);
852 cfg->linestack_backend = g_key_file_get_string(kf, "global", "linestack", NULL);
854 if (g_key_file_has_key(kf, "global", "report-time", NULL))
855 cfg->report_time = g_key_file_get_boolean(kf, "global", "report-time", NULL);
857 if (g_key_file_has_key(kf, "global", "motd-file", NULL))
858 cfg->motd_file = g_key_file_get_string(kf, "global", "motd-file", NULL);
860 cfg->motd_file = g_build_filename(SHAREDIR, "motd", NULL);
862 if (g_key_file_has_key(kf, "client", "charset", NULL))
863 cfg->client_charset = g_key_file_get_string(kf, "client", "charset", NULL);
864 else if (g_key_file_has_key(kf, "global", "client-charset", NULL))
865 cfg->client_charset = g_key_file_get_string(kf, "global", "client-charset", NULL);
867 cfg->client_charset = NULL;
869 if (g_key_file_has_key(kf, "global", "learn-nickserv", NULL))
870 cfg->learn_nickserv = g_key_file_get_boolean(kf, "global", "learn-nicksev", NULL);
872 cfg->learn_nickserv = TRUE;
874 if (g_key_file_has_key(kf, "global", "learn-network-name", NULL))
875 cfg->learn_network_name = g_key_file_get_boolean(kf, "global", "learn-network-name", NULL);
877 cfg->learn_network_name = TRUE;
879 if (!g_file_test(cfg->motd_file, G_FILE_TEST_EXISTS))
880 log_global(LOG_ERROR, "Can't open MOTD file '%s' for reading", cfg->motd_file);
882 if (g_key_file_has_key(kf, "admin", "without_privmsg", NULL)) {
883 if (g_key_file_get_boolean(kf, "admin", "without_privmsg", NULL)) {
884 cfg->admin_user = NULL;
886 cfg->admin_user = g_strdup("ctrlproxy");
888 g_key_file_remove_key(kf, "admin", "without_privmsg", NULL);
891 if (g_key_file_has_key(kf, "global", "admin-user", NULL)) {
892 cfg->admin_user = g_key_file_get_string(kf, "global", "admin-user", NULL);
895 cfg->admin_log = TRUE;
896 if (g_key_file_has_key(kf, "admin", "log", NULL) && !g_key_file_get_boolean(kf, "admin", "log", NULL))
897 cfg->admin_log = FALSE;
898 g_key_file_remove_key(kf, "admin", "log", NULL);
899 if (g_key_file_has_key(kf, "global", "admin-log", NULL) && !g_key_file_get_boolean(kf, "global", "admin-log", NULL))
900 cfg->admin_log = FALSE;
902 for (gl = cfg->networks; gl; gl = gl->next) {
903 struct network_config *nc = gl->data;
905 nc->autoconnect = FALSE;
908 config_load_networks(cfg);
909 config_load_listeners(cfg);
910 config_load_listeners_socks(cfg);
911 config_load_log(cfg);
912 config_load_auto_away(cfg);
915 autoconnect_list = g_key_file_get_string_list(kf, "global", "autoconnect", &size, NULL);
917 for (i = 0; i < size; i++) {
918 struct network_config *nc = find_create_network_config(cfg, autoconnect_list[i]);
921 nc->autoconnect = TRUE;
924 g_strfreev(autoconnect_list);
930 struct network_config *network_config_init(struct ctrlproxy_config *cfg)
932 struct network_config *s = g_new0(struct network_config, 1);
934 s->autoconnect = FALSE;
935 s->nick = g_strdup(g_get_user_name());
936 s->username = g_strdup(g_get_user_name());
937 g_assert(s->username != NULL && strlen(s->username) > 0);
938 s->fullname = g_strdup(g_get_real_name());
939 if (s->fullname == NULL ||
940 strlen(s->fullname) == 0) {
942 s->fullname = g_strdup(s->username);
944 s->reconnect_interval = DEFAULT_RECONNECT_INTERVAL;
947 cfg->networks = g_list_append(cfg->networks, s);
951 void free_config(struct ctrlproxy_config *cfg)
953 while (cfg->networks) {
954 struct network_config *nc = cfg->networks->data;
957 g_free(nc->fullname);
958 g_free(nc->username);
959 g_free(nc->password);
960 while (nc->channels) {
961 struct channel_config *cc = nc->channels->data;
964 nc->channels = g_list_remove(nc->channels, cc);
969 while (nc->type_settings.tcp_servers) {
970 struct tcp_server_config *tc = nc->type_settings.tcp_servers->data;
973 g_free(tc->bind_address);
974 g_free(tc->password);
975 nc->type_settings.tcp_servers = g_list_remove(nc->type_settings.tcp_servers, tc);
979 case NETWORK_VIRTUAL:
980 g_free(nc->type_settings.virtual_type);
982 case NETWORK_PROGRAM:
983 g_free(nc->type_settings.program_location);
985 case NETWORK_IOCHANNEL:
986 /* Nothing to free */
989 cfg->networks = g_list_remove(cfg->networks, nc);
990 if (nc->keyfile) g_key_file_free(nc->keyfile);
993 g_free(cfg->config_dir);
994 g_free(cfg->network_socket);
995 g_free(cfg->admin_socket);
996 g_free(cfg->replication);
997 g_free(cfg->linestack_backend);
998 g_free(cfg->motd_file);
999 g_free(cfg->admin_user);
1000 g_key_file_free(cfg->keyfile);
1004 gboolean create_configuration(const char *config_dir)
1006 struct global *global;
1008 struct listener_config *l;
1011 if (g_file_test(config_dir, G_FILE_TEST_IS_DIR)) {
1012 fprintf(stderr, "%s already exists\n", config_dir);
1016 if (g_mkdir(config_dir, 0700) != 0) {
1017 fprintf(stderr, "Can't create config directory '%s': %s\n", config_dir, strerror(errno));
1021 global = load_global(DEFAULT_CONFIG_DIR);
1022 if (global == NULL) {
1023 fprintf(stderr, "Unable to load default configuration '%s'\n", DEFAULT_CONFIG_DIR);
1026 global->config->config_dir = g_strdup(config_dir);
1027 save_configuration(global->config, config_dir);
1029 snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
1030 printf("Please specify port the administration interface should listen on.\n"
1031 "Prepend with a colon to listen on a specific address.\n"
1032 "Example: localhost:6668\n\nPort [%s]: ", port); fflush(stdout);
1033 if (!fgets(port, sizeof(port), stdin))
1034 snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
1036 if (port[strlen(port)-1] == '\n')
1037 port[strlen(port)-1] = '\0';
1039 if (strlen(port) == 0)
1040 snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
1042 l = g_new0(struct listener_config, 1);
1043 pass = getpass("Please specify a password for the administration interface: ");
1045 if (!strcmp(pass, "")) {
1046 fprintf(stderr, "Warning: no password specified. Authentication disabled!\n");
1051 global->config->listeners = g_list_append(global->config->listeners, l);