1 /*
2 settings.c : Irssi settings
3
4 Copyright (C) 1999 Timo Sirainen
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "commands.h"
24 #include "levels.h"
25 #include "misc.h"
26
27 #include "lib-config/iconfig.h"
28 #include "recode.h"
29 #include "settings.h"
30 #include "default-config.h"
31
32 #include <signal.h>
33
34 #define SETTINGS_AUTOSAVE_TIMEOUT (1000*60*60) /* 1 hour */
35
36 CONFIG_REC *mainconfig;
37
38 static GString *last_errors;
39 static GSList *last_invalid_modules;
40 static int fe_initialized;
41 static int config_changed; /* FIXME: remove after .98 (unless needed again) */
42 static unsigned int user_settings_changed;
43
44 static GHashTable *settings;
45 static int timeout_tag;
46
47 static int config_last_modifycounter;
48 static time_t config_last_mtime;
49 static long config_last_size;
50 static unsigned int config_last_checksum;
51
settings_get(const char * key,SettingType type)52 static SETTINGS_REC *settings_get(const char *key, SettingType type)
53 {
54 SETTINGS_REC *rec;
55
56 g_return_val_if_fail(key != NULL, NULL);
57
58 rec = g_hash_table_lookup(settings, key);
59 if (rec == NULL) {
60 g_warning("settings_get(%s) : not found", key);
61 return NULL;
62 }
63 if (type != SETTING_TYPE_ANY && rec->type != type) {
64 g_warning("settings_get(%s) : invalid type", key);
65 return NULL;
66 }
67
68 return rec;
69 }
70
71 static const char *
settings_get_str_type(const char * key,SettingType type)72 settings_get_str_type(const char *key, SettingType type)
73 {
74 SETTINGS_REC *rec;
75 CONFIG_NODE *node;
76
77 rec = settings_get(key, type);
78 if (rec == NULL) return NULL;
79
80 node = iconfig_node_traverse("settings", FALSE);
81 node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
82
83 return node == NULL ? rec->default_value.v_string :
84 config_node_get_str(node, key, rec->default_value.v_string);
85 }
86
settings_get_str(const char * key)87 const char *settings_get_str(const char *key)
88 {
89 return settings_get_str_type(key, SETTING_TYPE_ANY);
90 }
91
settings_get_int(const char * key)92 int settings_get_int(const char *key)
93 {
94 SETTINGS_REC *rec;
95 CONFIG_NODE *node;
96
97 rec = settings_get(key, SETTING_TYPE_INT);
98 if (rec == NULL) return 0;
99
100 node = iconfig_node_traverse("settings", FALSE);
101 node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
102
103 return node == NULL ? rec->default_value.v_int :
104 config_node_get_int(node, key, rec->default_value.v_int);
105 }
106
settings_get_bool(const char * key)107 int settings_get_bool(const char *key)
108 {
109 SETTINGS_REC *rec;
110 CONFIG_NODE *node;
111
112 rec = settings_get(key, SETTING_TYPE_BOOLEAN);
113 if (rec == NULL) return FALSE;
114
115 node = iconfig_node_traverse("settings", FALSE);
116 node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
117
118 return node == NULL ? rec->default_value.v_bool :
119 config_node_get_bool(node, key, rec->default_value.v_bool);
120 }
121
settings_get_time(const char * key)122 int settings_get_time(const char *key)
123 {
124 const char *str;
125 int msecs = 0;
126
127 str = settings_get_str_type(key, SETTING_TYPE_TIME);
128 if (str != NULL && !parse_time_interval(str, &msecs))
129 g_warning("settings_get_time(%s) : Invalid time '%s'", key, str);
130 return str == NULL ? 0 : msecs;
131 }
132
settings_get_level(const char * key)133 int settings_get_level(const char *key)
134 {
135 const char *str;
136
137 str = settings_get_str_type(key, SETTING_TYPE_LEVEL);
138 return str == NULL ? 0 : level2bits(str, NULL);
139 }
140
settings_get_size(const char * key)141 int settings_get_size(const char *key)
142 {
143 const char *str;
144 int bytes = 0;
145
146 str = settings_get_str_type(key, SETTING_TYPE_SIZE);
147 if (str != NULL && !parse_size(str, &bytes))
148 g_warning("settings_get_size(%s) : Invalid size '%s'", key, str);
149 return str == NULL ? 0 : bytes;
150 }
151
settings_get_choice(const char * key)152 int settings_get_choice(const char *key)
153 {
154 SETTINGS_REC *rec;
155 CONFIG_NODE *node;
156 char *str;
157 int index;
158
159 rec = settings_get(key, SETTING_TYPE_CHOICE);
160 if (rec == NULL) return -1;
161
162 node = iconfig_node_traverse("settings", FALSE);
163 node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
164
165 str = node == NULL ? rec->default_value.v_string :
166 config_node_get_str(node, key, rec->default_value.v_string);
167
168 if (str == NULL || (index = strarray_find(rec->choices, str)) < 0)
169 return rec->default_value.v_int;
170
171 return index;
172 }
173
settings_get_print(SETTINGS_REC * rec)174 char *settings_get_print(SETTINGS_REC *rec)
175 {
176 char *value = NULL;
177
178 switch(rec->type) {
179 case SETTING_TYPE_CHOICE:
180 value = g_strdup(rec->choices[settings_get_choice(rec->key)]);
181 break;
182 case SETTING_TYPE_BOOLEAN:
183 value = g_strdup(settings_get_bool(rec->key) ? "ON" : "OFF");
184 break;
185 case SETTING_TYPE_INT:
186 value = g_strdup_printf("%d", settings_get_int(rec->key));
187 break;
188 case SETTING_TYPE_STRING:
189 case SETTING_TYPE_TIME:
190 case SETTING_TYPE_LEVEL:
191 case SETTING_TYPE_SIZE:
192 case SETTING_TYPE_ANY:
193 value = g_strdup(settings_get_str(rec->key));
194 break;
195 }
196 return value;
197 }
198
settings_add(const char * module,const char * section,const char * key,SettingType type,const SettingValue * default_value,const char * choices)199 static void settings_add(const char *module, const char *section,
200 const char *key, SettingType type,
201 const SettingValue *default_value,
202 const char *choices)
203 {
204 SETTINGS_REC *rec;
205 char **choices_vec = NULL;
206
207 g_return_if_fail(key != NULL);
208 g_return_if_fail(section != NULL);
209
210 if (type == SETTING_TYPE_CHOICE) {
211 if (choices == NULL) {
212 g_warning("Trying to add setting '%s' with no choices.", key);
213 return;
214 }
215
216 choices_vec = g_strsplit(choices, ";", -1);
217
218 /* validate the default value */
219 if (default_value->v_int < 0 || default_value->v_int >= g_strv_length(choices_vec)) {
220 g_warning("Trying to add setting '%s' with an invalid default value.", key);
221 g_strfreev(choices_vec);
222 return;
223 }
224 }
225
226 rec = g_hash_table_lookup(settings, key);
227 if (rec != NULL) {
228 /* Already exists, make sure it's correct type */
229 if (rec->type != type) {
230 g_warning("Trying to add already existing "
231 "setting '%s' with different type.", key);
232 return;
233 }
234 rec->refcount++;
235 } else {
236 rec = g_new(SETTINGS_REC, 1);
237 rec->refcount = 1;
238 rec->module = g_strdup(module);
239 rec->key = g_strdup(key);
240 rec->section = g_strdup(section);
241 rec->type = type;
242
243 rec->default_value = *default_value;
244 rec->choices = choices_vec;
245 g_hash_table_insert(settings, rec->key, rec);
246 }
247 }
248
settings_add_str_module(const char * module,const char * section,const char * key,const char * def)249 void settings_add_str_module(const char *module, const char *section,
250 const char *key, const char *def)
251 {
252 SettingValue default_value;
253
254 memset(&default_value, 0, sizeof(default_value));
255 default_value.v_string = g_strdup(def);
256 settings_add(module, section, key, SETTING_TYPE_STRING, &default_value, NULL);
257 }
258
settings_add_choice_module(const char * module,const char * section,const char * key,int def,const char * choices)259 void settings_add_choice_module(const char *module, const char *section,
260 const char *key, int def, const char *choices)
261 {
262 SettingValue default_value;
263
264 memset(&default_value, 0, sizeof(default_value));
265 default_value.v_int = def;
266 settings_add(module, section, key, SETTING_TYPE_CHOICE, &default_value, choices);
267 }
268
settings_add_int_module(const char * module,const char * section,const char * key,int def)269 void settings_add_int_module(const char *module, const char *section,
270 const char *key, int def)
271 {
272 SettingValue default_value;
273
274 memset(&default_value, 0, sizeof(default_value));
275 default_value.v_int = def;
276 settings_add(module, section, key, SETTING_TYPE_INT, &default_value, NULL);
277 }
278
settings_add_bool_module(const char * module,const char * section,const char * key,int def)279 void settings_add_bool_module(const char *module, const char *section,
280 const char *key, int def)
281 {
282 SettingValue default_value;
283
284 memset(&default_value, 0, sizeof(default_value));
285 default_value.v_bool = def;
286 settings_add(module, section, key, SETTING_TYPE_BOOLEAN, &default_value, NULL);
287 }
288
settings_add_time_module(const char * module,const char * section,const char * key,const char * def)289 void settings_add_time_module(const char *module, const char *section,
290 const char *key, const char *def)
291 {
292 SettingValue default_value;
293
294 memset(&default_value, 0, sizeof(default_value));
295 default_value.v_string = g_strdup(def);
296 settings_add(module, section, key, SETTING_TYPE_TIME, &default_value, NULL);
297 }
298
settings_add_level_module(const char * module,const char * section,const char * key,const char * def)299 void settings_add_level_module(const char *module, const char *section,
300 const char *key, const char *def)
301 {
302 SettingValue default_value;
303
304 memset(&default_value, 0, sizeof(default_value));
305 default_value.v_string = g_strdup(def);
306 settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value, NULL);
307 }
308
settings_add_size_module(const char * module,const char * section,const char * key,const char * def)309 void settings_add_size_module(const char *module, const char *section,
310 const char *key, const char *def)
311 {
312 SettingValue default_value;
313
314 memset(&default_value, 0, sizeof(default_value));
315 default_value.v_string = g_strdup(def);
316 settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value, NULL);
317 }
318
settings_destroy(SETTINGS_REC * rec)319 static void settings_destroy(SETTINGS_REC *rec)
320 {
321 if (rec->type != SETTING_TYPE_INT &&
322 rec->type != SETTING_TYPE_BOOLEAN &&
323 rec->type != SETTING_TYPE_CHOICE)
324 g_free(rec->default_value.v_string);
325 g_strfreev(rec->choices);
326 g_free(rec->module);
327 g_free(rec->section);
328 g_free(rec->key);
329 g_free(rec);
330 }
331
settings_unref(SETTINGS_REC * rec,int remove_hash)332 static void settings_unref(SETTINGS_REC *rec, int remove_hash)
333 {
334 if (--rec->refcount == 0) {
335 if (remove_hash)
336 g_hash_table_remove(settings, rec->key);
337 settings_destroy(rec);
338 }
339 }
340
settings_remove(const char * key)341 void settings_remove(const char *key)
342 {
343 SETTINGS_REC *rec;
344
345 g_return_if_fail(key != NULL);
346
347 rec = g_hash_table_lookup(settings, key);
348 if (rec != NULL)
349 settings_unref(rec, TRUE);
350 }
351
settings_remove_hash(const char * key,SETTINGS_REC * rec,const char * module)352 static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
353 const char *module)
354 {
355 if (g_strcmp0(rec->module, module) == 0) {
356 settings_unref(rec, FALSE);
357 return TRUE;
358 }
359
360 return FALSE;
361 }
362
settings_remove_module(const char * module)363 void settings_remove_module(const char *module)
364 {
365 g_hash_table_foreach_remove(settings,
366 (GHRFunc) settings_remove_hash,
367 (void *) module);
368 }
369
settings_get_node(const char * key)370 static CONFIG_NODE *settings_get_node(const char *key)
371 {
372 SETTINGS_REC *rec;
373 CONFIG_NODE *node;
374
375 g_return_val_if_fail(key != NULL, NULL);
376
377 rec = g_hash_table_lookup(settings, key);
378 if (rec == NULL) {
379 g_warning("Changing unknown setting '%s'", key);
380 return NULL;
381 }
382
383 node = iconfig_node_traverse("settings", TRUE);
384 return iconfig_node_section(node, rec->module, NODE_TYPE_BLOCK);
385 }
386
settings_set_choice(const char * key,const char * value)387 gboolean settings_set_choice(const char *key, const char *value)
388 {
389 SETTINGS_REC *rec;
390
391 rec = settings_get_record(key);
392
393 if (rec != NULL && strarray_find(rec->choices, value) < 0)
394 return FALSE;
395
396 settings_set_str(key, value);
397
398 return TRUE;
399 }
400
settings_set_str(const char * key,const char * value)401 void settings_set_str(const char *key, const char *value)
402 {
403 iconfig_node_set_str(settings_get_node(key), key, value);
404 }
405
settings_set_int(const char * key,int value)406 void settings_set_int(const char *key, int value)
407 {
408 iconfig_node_set_int(settings_get_node(key), key, value);
409 }
410
settings_set_bool(const char * key,int value)411 void settings_set_bool(const char *key, int value)
412 {
413 iconfig_node_set_bool(settings_get_node(key), key, value);
414 }
415
settings_set_time(const char * key,const char * value)416 gboolean settings_set_time(const char *key, const char *value)
417 {
418 int msecs;
419
420 if (!parse_time_interval(value, &msecs))
421 return FALSE;
422
423 iconfig_node_set_str(settings_get_node(key), key, value);
424 return TRUE;
425 }
426
settings_set_level(const char * key,const char * value)427 gboolean settings_set_level(const char *key, const char *value)
428 {
429 int iserror;
430
431 (void)level2bits(value, &iserror);
432 if (iserror)
433 return FALSE;
434
435 iconfig_node_set_str(settings_get_node(key), key, value);
436 return TRUE;
437 }
438
settings_set_size(const char * key,const char * value)439 gboolean settings_set_size(const char *key, const char *value)
440 {
441 int size;
442
443 if (!parse_size(value, &size))
444 return FALSE;
445
446 iconfig_node_set_str(settings_get_node(key), key, value);
447 return TRUE;
448 }
449
settings_get_type(const char * key)450 SettingType settings_get_type(const char *key)
451 {
452 SETTINGS_REC *rec;
453
454 g_return_val_if_fail(key != NULL, SETTING_TYPE_ANY);
455
456 rec = g_hash_table_lookup(settings, key);
457 return rec == NULL ? SETTING_TYPE_ANY : rec->type;
458 }
459
460 /* Get the record of the setting */
settings_get_record(const char * key)461 SETTINGS_REC *settings_get_record(const char *key)
462 {
463 g_return_val_if_fail(key != NULL, NULL);
464
465 return g_hash_table_lookup(settings, key);
466 }
467
sig_init_userinfo_changed(gpointer changedp)468 static void sig_init_userinfo_changed(gpointer changedp)
469 {
470 user_settings_changed |= GPOINTER_TO_UINT(changedp);
471 }
472
sig_init_finished(void)473 static void sig_init_finished(void)
474 {
475 fe_initialized = TRUE;
476 if (last_errors != NULL) {
477 signal_emit("settings errors", 1, last_errors->str);
478 g_string_free(last_errors, TRUE);
479 }
480
481 if (config_changed) {
482 /* some backwards compatibility changes were made to
483 config file, reload it */
484 g_warning("Some settings were automatically "
485 "updated, please /SAVE");
486 signal_emit("setup changed", 0);
487 }
488
489 signal_emit("settings userinfo changed", 1, GUINT_TO_POINTER(user_settings_changed));
490 }
491
settings_clean_invalid_module(const char * module)492 static void settings_clean_invalid_module(const char *module)
493 {
494 CONFIG_NODE *node;
495 SETTINGS_REC *set;
496 GSList *tmp, *next;
497
498 node = iconfig_node_traverse("settings", FALSE);
499 if (node == NULL) return;
500
501 node = iconfig_node_section(node, module, -1);
502 if (node == NULL) return;
503
504 for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) {
505 CONFIG_NODE *subnode = tmp->data;
506 next = config_node_next(tmp);
507
508 set = g_hash_table_lookup(settings, subnode->key);
509 if (set == NULL || g_strcmp0(set->module, module) != 0)
510 iconfig_node_remove(node, subnode);
511 }
512 }
513
514 /* remove all invalid settings from config file. works only with the
515 modules that have already called settings_check() */
settings_clean_invalid(void)516 void settings_clean_invalid(void)
517 {
518 while (last_invalid_modules != NULL) {
519 char *module = last_invalid_modules->data;
520
521 settings_clean_invalid_module(module);
522
523 last_invalid_modules =
524 g_slist_remove(last_invalid_modules, module);
525 g_free(module);
526 }
527 }
528
backwards_compatibility(const char * module,CONFIG_NODE * node,CONFIG_NODE * parent)529 static int backwards_compatibility(const char *module, CONFIG_NODE *node,
530 CONFIG_NODE *parent)
531 {
532 const char *new_key, *new_module;
533 CONFIG_NODE *new_node;
534 char *new_value;
535
536 new_value = NULL; new_key = NULL; new_module = NULL;
537
538 /* fe-text term_type -> fe-common/core term_charset - for 0.8.10-> */
539 if (g_strcmp0(module, "fe-text") == 0) {
540 if (g_ascii_strcasecmp(node->key, "term_type") == 0 ||
541 /* kludge for cvs-version where term_charset was in fe-text */
542 g_ascii_strcasecmp(node->key, "term_charset") == 0) {
543 new_module = "fe-common/core";
544 new_key = "term_charset";
545 new_value = !is_valid_charset(node->value) ? NULL :
546 g_strdup(node->value);
547 new_node = iconfig_node_traverse("settings", FALSE);
548 new_node = new_node == NULL ? NULL :
549 iconfig_node_section(new_node, new_module, -1);
550
551 config_node_set_str(mainconfig, new_node,
552 new_key, new_value);
553 /* remove old */
554 config_node_set_str(mainconfig, parent,
555 node->key, NULL);
556 g_free(new_value);
557 config_changed = TRUE;
558 return new_key != NULL;
559 } else if (g_ascii_strcasecmp(node->key, "actlist_moves") == 0 &&
560 node->value != NULL && g_ascii_strcasecmp(node->value, "yes") == 0) {
561 config_node_set_str(mainconfig, parent, "actlist_sort", "recent");
562 config_node_set_str(mainconfig, parent, node->key, NULL);
563 config_changed = TRUE;
564 return TRUE;
565 }
566 }
567 return new_key != NULL;
568 }
569
570 /* verify that all settings in config file for `module' are actually found
571 from /SET list */
settings_check_module(const char * module)572 void settings_check_module(const char *module)
573 {
574 SETTINGS_REC *set;
575 CONFIG_NODE *node, *parent;
576 GString *errors;
577 GSList *tmp, *next;
578 int count;
579
580 g_return_if_fail(module != NULL);
581
582 node = iconfig_node_traverse("settings", FALSE);
583 node = node == NULL ? NULL : iconfig_node_section(node, module, -1);
584 if (node == NULL) return;
585
586 errors = g_string_new(NULL);
587 g_string_printf(errors, "Unknown settings in configuration "
588 "file for module %s:", module);
589
590 count = 0;
591 parent = node;
592 tmp = config_node_first(node->value);
593 for (; tmp != NULL; tmp = next) {
594 node = tmp->data;
595 next = config_node_next(tmp);
596 if (node->key == NULL) continue;
597
598 set = g_hash_table_lookup(settings, node->key);
599 if (backwards_compatibility(module, node, parent))
600 continue;
601
602 if (set == NULL || g_strcmp0(set->module, module) != 0) {
603 g_string_append_printf(errors, " %s", node->key);
604 count++;
605 }
606 }
607 if (count > 0) {
608 if (gslist_find_icase_string(last_invalid_modules,
609 module) == NULL) {
610 /* mark this module having invalid settings */
611 last_invalid_modules =
612 g_slist_append(last_invalid_modules,
613 g_strdup(module));
614 }
615 if (fe_initialized)
616 signal_emit("settings errors", 1, errors->str);
617 else {
618 if (last_errors == NULL)
619 last_errors = g_string_new(NULL);
620 else
621 g_string_append_c(last_errors, '\n');
622 g_string_append(last_errors, errors->str);
623 }
624 }
625 g_string_free(errors, TRUE);
626 }
627
settings_compare(SETTINGS_REC * v1,SETTINGS_REC * v2)628 static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
629 {
630 int cmp = g_strcmp0(v1->section, v2->section);
631 if (!cmp)
632 cmp = g_strcmp0(v1->key, v2->key);
633 return cmp;
634 }
635
settings_hash_get(const char * key,SETTINGS_REC * rec,GSList ** list)636 static void settings_hash_get(const char *key, SETTINGS_REC *rec,
637 GSList **list)
638 {
639 *list = g_slist_insert_sorted(*list, rec,
640 (GCompareFunc) settings_compare);
641 }
642
settings_get_sorted(void)643 GSList *settings_get_sorted(void)
644 {
645 GSList *list;
646
647 list = NULL;
648 g_hash_table_foreach(settings, (GHFunc) settings_hash_get, &list);
649 return list;
650 }
651
sig_term(int n)652 void sig_term(int n)
653 {
654 /* if we get SIGTERM after this, just die instead of coming back here. */
655 signal(SIGTERM, SIG_DFL);
656
657 /* quit from all servers too.. */
658 signal_emit("command quit", 1, "");
659
660 /* and die */
661 raise(SIGTERM);
662 }
663
664 /* Yes, this is my own stupid checksum generator, some "real" algorithm
665 would be nice but would just take more space without much real benefit */
file_checksum(const char * fname)666 static unsigned int file_checksum(const char *fname)
667 {
668 char buf[512];
669 int f, ret, n;
670 unsigned int checksum = 0;
671
672 f = open(fname, O_RDONLY);
673 if (f == -1) return 0;
674
675 n = 0;
676 while ((ret = read(f, buf, sizeof(buf))) > 0) {
677 while (ret-- > 0)
678 checksum += buf[ret] << ((n++ & 3)*8);
679 }
680 close(f);
681 return checksum;
682 }
683
irssi_config_save_state(const char * fname)684 static void irssi_config_save_state(const char *fname)
685 {
686 struct stat statbuf;
687
688 g_return_if_fail(fname != NULL);
689
690 if (stat(fname, &statbuf) != 0)
691 return;
692
693 /* save modify time, file size and checksum */
694 config_last_mtime = statbuf.st_mtime;
695 config_last_size = statbuf.st_size;
696 config_last_checksum = file_checksum(fname);
697 }
698
irssi_config_is_changed(const char * fname)699 int irssi_config_is_changed(const char *fname)
700 {
701 struct stat statbuf;
702
703 if (fname == NULL)
704 fname = mainconfig->fname;
705
706 if (stat(fname, &statbuf) != 0)
707 return FALSE;
708
709 return config_last_mtime != statbuf.st_mtime &&
710 (config_last_size != statbuf.st_size ||
711 config_last_checksum != file_checksum(fname));
712 }
713
parse_configfile(const char * fname)714 static CONFIG_REC *parse_configfile(const char *fname)
715 {
716 CONFIG_REC *config;
717 struct stat statbuf;
718 const char *path;
719 char *str;
720
721 if (fname == NULL)
722 fname = get_irssi_config();
723
724 if (stat(fname, &statbuf) == 0)
725 path = fname;
726 else {
727 /* user configuration file not found, use the default one
728 from sysconfdir */
729 path = SYSCONFDIR"/"IRSSI_GLOBAL_CONFIG;
730 if (stat(path, &statbuf) != 0) {
731 /* no configuration file in sysconfdir ..
732 use the build-in configuration */
733 path = NULL;
734 }
735 }
736
737 config = config_open(path, -1);
738 if (config == NULL) {
739 str = g_strdup_printf("Error opening configuration file %s: %s",
740 path, g_strerror(errno));
741 signal_emit("gui dialog", 2, "error", str);
742 g_free(str);
743
744 config = config_open(NULL, -1);
745 }
746
747 if (config->fname != NULL)
748 config_parse(config);
749 else
750 config_parse_data(config, default_config, "internal");
751
752 config_change_file_name(config, fname, 0660);
753 irssi_config_save_state(fname);
754 return config;
755 }
756
init_configfile(void)757 static void init_configfile(void)
758 {
759 struct stat statbuf;
760 char *str;
761
762 if (stat(get_irssi_dir(), &statbuf) != 0) {
763 /* ~/.irssi not found, create it. */
764 if (g_mkdir_with_parents(get_irssi_dir(), 0700) != 0) {
765 g_error("Couldn't create %s directory: %s",
766 get_irssi_dir(), g_strerror(errno));
767 }
768 } else if (!S_ISDIR(statbuf.st_mode)) {
769 g_error("%s is not a directory.\n"
770 "You should remove it with command: rm %s",
771 get_irssi_dir(), get_irssi_dir());
772 }
773
774 mainconfig = parse_configfile(NULL);
775 config_last_modifycounter = mainconfig->modifycounter;
776
777 /* any errors? */
778 if (config_last_error(mainconfig) != NULL) {
779 str = g_strdup_printf("Ignored errors in configuration file:\n%s",
780 config_last_error(mainconfig));
781 signal_emit("gui dialog", 2, "error", str);
782 g_free(str);
783 }
784
785 signal(SIGTERM, sig_term);
786 }
787
settings_reread(const char * fname)788 int settings_reread(const char *fname)
789 {
790 CONFIG_REC *tempconfig;
791 char *str;
792
793 str = fname == NULL ? NULL : convert_home(fname);
794 tempconfig = parse_configfile(str);
795 g_free_not_null(str);
796
797 if (tempconfig == NULL) {
798 signal_emit("gui dialog", 2, "error", g_strerror(errno));
799 return FALSE;
800 }
801
802 if (config_last_error(tempconfig) != NULL) {
803 str = g_strdup_printf("Errors in configuration file:\n%s",
804 config_last_error(tempconfig));
805 signal_emit("gui dialog", 2, "error", str);
806 g_free(str);
807
808 config_close(tempconfig);
809 return FALSE;
810 }
811
812 config_close(mainconfig);
813 mainconfig = tempconfig;
814 config_last_modifycounter = mainconfig->modifycounter;
815
816 signal_emit("setup changed", 0);
817 signal_emit("setup reread", 1, mainconfig->fname);
818 return TRUE;
819 }
820
settings_save(const char * fname,int autosave)821 int settings_save(const char *fname, int autosave)
822 {
823 char *str;
824 int error;
825
826 if (fname == NULL)
827 fname = mainconfig->fname;
828
829 error = config_write(mainconfig, fname, 0660) != 0;
830 irssi_config_save_state(fname);
831 config_last_modifycounter = mainconfig->modifycounter;
832 if (error) {
833 str = g_strdup_printf("Couldn't save configuration file: %s",
834 config_last_error(mainconfig));
835 signal_emit("gui dialog", 2, "error", str);
836 g_free(str);
837 }
838 signal_emit("setup saved", 2, fname, GINT_TO_POINTER(autosave));
839 return !error;
840 }
841
sig_autosave(void)842 static int sig_autosave(void)
843 {
844 char *fname, *str;
845
846 if (!settings_get_bool("settings_autosave") ||
847 config_last_modifycounter == mainconfig->modifycounter)
848 return 1;
849
850 if (!irssi_config_is_changed(NULL))
851 settings_save(NULL, TRUE);
852 else {
853 fname = g_strconcat(mainconfig->fname, ".autosave", NULL);
854 str = g_strdup_printf("Configuration file was modified "
855 "while irssi was running. Saving "
856 "configuration to file '%s' instead. "
857 "Use /SAVE or /RELOAD to get rid of "
858 "this message.", fname);
859 signal_emit("gui dialog", 2, "warning", str);
860 g_free(str);
861
862 settings_save(fname, TRUE);
863 g_free(fname);
864 }
865
866 return 1;
867 }
868
settings_init(void)869 void settings_init(void)
870 {
871 settings = g_hash_table_new((GHashFunc) g_istr_hash,
872 (GCompareFunc) g_istr_equal);
873
874 last_errors = NULL;
875 last_invalid_modules = NULL;
876 fe_initialized = FALSE;
877 config_changed = FALSE;
878
879 config_last_mtime = 0;
880 config_last_modifycounter = 0;
881 init_configfile();
882
883 settings_add_bool("misc", "settings_autosave", TRUE);
884 timeout_tag = g_timeout_add(SETTINGS_AUTOSAVE_TIMEOUT,
885 (GSourceFunc) sig_autosave, NULL);
886 signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
887 signal_add("irssi init userinfo changed", (SIGNAL_FUNC) sig_init_userinfo_changed);
888 signal_add("gui exit", (SIGNAL_FUNC) sig_autosave);
889 }
890
settings_hash_free(const char * key,SETTINGS_REC * rec)891 static void settings_hash_free(const char *key, SETTINGS_REC *rec)
892 {
893 settings_destroy(rec);
894 }
895
settings_deinit(void)896 void settings_deinit(void)
897 {
898 g_source_remove(timeout_tag);
899 signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
900 signal_remove("irssi init userinfo changed", (SIGNAL_FUNC) sig_init_userinfo_changed);
901 signal_remove("gui exit", (SIGNAL_FUNC) sig_autosave);
902
903 g_slist_foreach(last_invalid_modules, (GFunc) g_free, NULL);
904 g_slist_free(last_invalid_modules);
905
906 g_hash_table_foreach(settings, (GHFunc) settings_hash_free, NULL);
907 g_hash_table_destroy(settings);
908
909 if (mainconfig != NULL) config_close(mainconfig);
910 }
911