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