addition of silc.css
[crypto.git] / apps / irssi / src / fe-common / core / fe-log.c
1 /*
2  fe-log.c : irssi
3
4     Copyright (C) 1999-2000 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
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "module-formats.h"
23 #include "signals.h"
24 #include "commands.h"
25 #include "servers.h"
26 #include "levels.h"
27 #include "misc.h"
28 #include "log.h"
29 #include "special-vars.h"
30 #include "settings.h"
31
32 #include "fe-windows.h"
33 #include "window-items.h"
34 #include "formats.h"
35 #include "themes.h"
36 #include "printtext.h"
37
38 /* close autologs after 5 minutes of inactivity */
39 #define AUTOLOG_INACTIVITY_CLOSE (60*5)
40
41 #define LOG_DIR_CREATE_MODE 0770
42
43 static int autolog_level;
44 static int autoremove_tag;
45 static const char *autolog_path;
46
47 static THEME_REC *log_theme;
48 static int skip_next_printtext;
49 static const char *log_theme_name;
50
51 static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
52 {
53         char **tmp, **items;
54
55         g_return_if_fail(log != NULL);
56         g_return_if_fail(targets != NULL);
57
58         items = g_strsplit(targets, " ", -1);
59
60         for (tmp = items; *tmp != NULL; tmp++)
61                 log_item_add(log, LOG_ITEM_TARGET, *tmp, tag);
62
63         g_strfreev(items);
64 }
65
66 /* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>]
67                     [-targets <targets>] <fname> [<levels>] */
68 static void cmd_log_open(const char *data)
69 {
70         SERVER_REC *server;
71         GHashTable *optlist;
72         char *targetarg, *fname, *levels, *servertag;
73         void *free_arg;
74         char window[MAX_INT_STRLEN];
75         LOG_REC *log;
76         int level;
77
78         if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
79                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS,
80                             "log open", &optlist, &fname, &levels))
81                 return;
82         if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
83
84         level = level2bits(levels);
85         log = log_create_rec(fname, level != 0 ? level : MSGLEVEL_ALL);
86
87         /* -<server tag> */
88         server = cmd_options_get_server("log open", optlist, NULL);
89         servertag = server == NULL ? NULL : server->tag;
90
91         if (g_hash_table_lookup(optlist, "window")) {
92                 /* log by window ref# */
93                 ltoa(window, active_win->refnum);
94                 log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window,
95                              servertag);
96         } else {
97                 targetarg = g_hash_table_lookup(optlist, "targets");
98                 if (targetarg != NULL && *targetarg != '\0')
99                         log_add_targets(log, targetarg, servertag);
100         }
101
102         if (g_hash_table_lookup(optlist, "autoopen"))
103                 log->autoopen = TRUE;
104
105         log_update(log);
106
107         if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) {
108                 /* start logging */
109                 if (log_start_logging(log)) {
110                         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
111                                     TXT_LOG_OPENED, fname);
112                 } else {
113                         log_close(log);
114                 }
115         }
116
117         cmd_params_free(free_arg);
118 }
119
120 static LOG_REC *log_find_from_data(const char *data)
121 {
122         GSList *tmp;
123
124         if (!is_numeric(data, ' '))
125                 return log_find(data);
126
127         /* with index number */
128         tmp = g_slist_nth(logs, atoi(data)-1);
129         return tmp == NULL ? NULL : tmp->data;
130 }
131
132 /* SYNTAX: LOG CLOSE <id>|<file> */
133 static void cmd_log_close(const char *data)
134 {
135         LOG_REC *log;
136
137         log = log_find_from_data(data);
138         if (log == NULL)
139                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
140         else {
141                 log_close(log);
142                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
143         }
144 }
145
146 /* SYNTAX: LOG START <id>|<file> */
147 static void cmd_log_start(const char *data)
148 {
149         LOG_REC *log;
150
151         log = log_find_from_data(data);
152         if (log != NULL) {
153                 log_start_logging(log);
154                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, data);
155         }
156 }
157
158 /* SYNTAX: LOG STOP <id>|<file> */
159 static void cmd_log_stop(const char *data)
160 {
161         LOG_REC *log;
162
163         log = log_find_from_data(data);
164         if (log == NULL || log->handle == -1)
165                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
166         else {
167                 log_stop_logging(log);
168                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
169         }
170 }
171
172 static char *log_items_get_list(LOG_REC *log)
173 {
174         GSList *tmp;
175         GString *str;
176         char *ret;
177
178         g_return_val_if_fail(log != NULL, NULL);
179         g_return_val_if_fail(log->items != NULL, NULL);
180
181         str = g_string_new(NULL);
182         for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
183                 LOG_ITEM_REC *rec = tmp->data;
184
185                 g_string_sprintfa(str, "%s, ", rec->name);
186         }
187         g_string_truncate(str, str->len-2);
188
189         ret = str->str;
190         g_string_free(str, FALSE);
191         return ret;
192 }
193
194 static void cmd_log_list(void)
195 {
196         GSList *tmp;
197         char *levelstr, *items;
198         int index;
199
200         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_HEADER);
201         for (tmp = logs, index = 1; tmp != NULL; tmp = tmp->next, index++) {
202                 LOG_REC *rec = tmp->data;
203
204                 levelstr = bits2level(rec->level);
205                 items = rec->items == NULL ? NULL :
206                         log_items_get_list(rec);
207
208                 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST,
209                             index, rec->fname, items != NULL ? items : "",
210                             levelstr, rec->autoopen ? " -autoopen" : "");
211
212                 g_free_not_null(items);
213                 g_free(levelstr);
214         }
215         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_FOOTER);
216 }
217
218 static void cmd_log(const char *data, SERVER_REC *server, void *item)
219 {
220         if (*data == '\0')
221                 cmd_log_list();
222         else
223                 command_runsub("log", data, server, item);
224 }
225
226 static LOG_REC *logs_find_item(int type, const char *item,
227                                const char *servertag, LOG_ITEM_REC **ret_item)
228 {
229         LOG_ITEM_REC *logitem;
230         GSList *tmp;
231
232         for (tmp = logs; tmp != NULL; tmp = tmp->next) {
233                 LOG_REC *log = tmp->data;
234
235                 logitem = log_item_find(log, type, item, servertag);
236                 if (logitem != NULL) {
237                         if (ret_item != NULL) *ret_item = logitem;
238                         return log;
239                 }
240         }
241
242         return NULL;
243 }
244
245 /* SYNTAX: WINDOW LOG on|off|toggle [<filename>] */
246 static void cmd_window_log(const char *data)
247 {
248         LOG_REC *log;
249         char *set, *fname, window[MAX_INT_STRLEN];
250         void *free_arg;
251         int open_log, close_log;
252
253         if (!cmd_get_params(data, &free_arg, 2, &set, &fname))
254                 return;
255
256         ltoa(window, active_win->refnum);
257         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
258
259         open_log = close_log = FALSE;
260         if (g_strcasecmp(set, "ON") == 0)
261                 open_log = TRUE;
262         else if (g_strcasecmp(set, "OFF") == 0) {
263                 close_log = TRUE;
264         } else if (g_strcasecmp(set, "TOGGLE") == 0) {
265                 open_log = log == NULL;
266                 close_log = log != NULL;
267         } else {
268                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_TOGGLE);
269                 cmd_params_free(free_arg);
270                 return;
271         }
272
273         if (open_log && log == NULL) {
274                 /* irc.log.<windowname> or irc.log.Window<ref#> */
275                 fname = *fname != '\0' ? g_strdup(fname) :
276                         g_strdup_printf("~/irc.log.%s%s",
277                                         active_win->name != NULL ? active_win->name : "Window",
278                                         active_win->name != NULL ? "" : window);
279                 log = log_create_rec(fname, MSGLEVEL_ALL);
280                 log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
281                 log_update(log);
282                 g_free(fname);
283         }
284
285         if (open_log && log != NULL) {
286                 log_start_logging(log);
287                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, log->fname);
288         } else if (close_log && log != NULL && log->handle != -1) {
289                 log_stop_logging(log);
290                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, log->fname);
291         }
292
293         cmd_params_free(free_arg);
294 }
295
296 /* Create log file entry to window, but don't start logging */
297 /* SYNTAX: WINDOW LOGFILE <file> */
298 static void cmd_window_logfile(const char *data)
299 {
300         LOG_REC *log;
301         char window[MAX_INT_STRLEN];
302
303         ltoa(window, active_win->refnum);
304         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
305
306         if (log != NULL) {
307                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE_LOGGING);
308                 return;
309         }
310
311         log = log_create_rec(data, MSGLEVEL_ALL);
312         log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
313         log_update(log);
314
315         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE, data);
316 }
317
318 /* window's refnum changed - update the logs to log the new window refnum */
319 static void sig_window_refnum_changed(WINDOW_REC *window, gpointer old_refnum)
320 {
321         char winnum[MAX_INT_STRLEN];
322         LOG_REC *log;
323         LOG_ITEM_REC *item;
324
325         ltoa(winnum, GPOINTER_TO_INT(old_refnum));
326         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, winnum, NULL, &item);
327
328         if (log != NULL) {
329                 ltoa(winnum, window->refnum);
330
331                 g_free(item->name);
332                 item->name = g_strdup(winnum);
333         }
334 }
335
336 static void sig_server_disconnected(SERVER_REC *server)
337 {
338         LOG_ITEM_REC *logitem;
339         GSList *tmp, *next;
340
341         for (tmp = logs; tmp != NULL; tmp = next) {
342                 LOG_REC *log = tmp->data;
343                 next = tmp->next;
344
345                 if (!log->temp || log->items == NULL)
346                         continue;
347
348                 logitem = log->items->data;
349                 if (logitem->type == LOG_ITEM_TARGET &&
350                     g_strcasecmp(logitem->servertag, server->tag) == 0)
351                         log_close(log);
352         }
353 }
354
355 static void autologs_close_all(void)
356 {
357         GSList *tmp, *next;
358
359         for (tmp = logs; tmp != NULL; tmp = next) {
360                 LOG_REC *rec = tmp->data;
361
362                 next = tmp->next;
363                 if (rec->temp) log_close(rec);
364         }
365 }
366
367 static void autolog_open(SERVER_REC *server, const char *target)
368 {
369         LOG_REC *log;
370         char *fname, *dir, *fixed_target, *tag;
371
372         tag = server == NULL ? NULL : server->tag;
373         log = logs_find_item(LOG_ITEM_TARGET, target, tag, NULL);
374         if (log != NULL && !log->failed) {
375                 log_start_logging(log);
376                 return;
377         }
378
379         /* '/' -> '_' - don't even accidentally try to log to
380            #../../../file if you happen to join to such channel.. */
381         fixed_target = g_strdup(target);
382         replace_chars(fixed_target, '/', '_');
383         fname = parse_special_string(autolog_path, server, NULL,
384                                      fixed_target, NULL, 0);
385         g_free(fixed_target);
386
387         if (log_find(fname) == NULL) {
388                 log = log_create_rec(fname, autolog_level);
389                 log_item_add(log, LOG_ITEM_TARGET, target, tag);
390
391                 dir = g_dirname(log->real_fname);
392                 mkpath(dir, LOG_DIR_CREATE_MODE);
393                 g_free(dir);
394
395                 log->temp = TRUE;
396                 log_update(log);
397                 log_start_logging(log);
398         }
399         g_free(fname);
400 }
401
402 static void autolog_open_check(SERVER_REC *server, const char *target,
403                                int level)
404 {
405         char **targets, **tmp;
406
407         if (level == MSGLEVEL_PARTS || /* FIXME: kind of a kludge, but we don't want to reopen logs when we're parting the channel with /WINDOW CLOSE.. */
408             (autolog_level & level) == 0 || target == NULL || *target == '\0')
409                 return;
410
411         /* there can be multiple targets separated with comma */
412         targets = g_strsplit(target, ",", -1);
413         for (tmp = targets; *tmp != NULL; tmp++)
414                 autolog_open(server, *tmp);
415         g_strfreev(targets);
416 }
417
418 static void log_single_line(WINDOW_REC *window, void *server,
419                             const char *target, int level, const char *text)
420 {
421         char windownum[MAX_INT_STRLEN];
422         char **targets, **tmp;
423         LOG_REC *log;
424
425         /* save to log created with /WINDOW LOG */
426         ltoa(windownum, window->refnum);
427         log = logs_find_item(LOG_ITEM_WINDOW_REFNUM,
428                              windownum, NULL, NULL);
429         if (log != NULL) log_write_rec(log, text, level);
430
431         if (target == NULL)
432                 log_file_write(server, NULL, level, text, FALSE);
433         else {
434                 /* there can be multiple items separated with comma */
435                 targets = g_strsplit(target, ",", -1);
436                 for (tmp = targets; *tmp != NULL; tmp++)
437                         log_file_write(server, *tmp, level, text, FALSE);
438                 g_strfreev(targets);
439         }
440 }
441
442 static void log_line(WINDOW_REC *window, SERVER_REC *server,
443                      const char *target, int level, const char *text)
444 {
445         char **lines, **tmp;
446
447         if (level == MSGLEVEL_NEVER)
448                 return;
449
450         /* let autolog open the log records */
451         autolog_open_check(server, target, level);
452
453         if (logs == NULL)
454                 return;
455
456         /* text may contain one or more lines, log wants to eat them one
457            line at a time */
458         lines = g_strsplit(text, "\n", -1);
459         for (tmp = lines; *tmp != NULL; tmp++)
460                 log_single_line(window, server, target, level, *tmp);
461         g_strfreev(lines);
462 }
463
464 static void sig_printtext_stripped(TEXT_DEST_REC *dest, const char *text)
465 {
466         if (skip_next_printtext) {
467                 skip_next_printtext = FALSE;
468                 return;
469         }
470
471         log_line(dest->window, dest->server, dest->target,
472                  dest->level, text);
473 }
474
475 static void sig_print_format(THEME_REC *theme, const char *module,
476                              TEXT_DEST_REC *dest, void *formatnum, char **args)
477 {
478         char *str, *stripped, *linestart, *tmp;
479
480         if (log_theme == NULL) {
481                 /* theme isn't loaded for some reason (/reload destroys it),
482                    reload it. */
483                 log_theme = theme_load(log_theme_name);
484                 if (log_theme == NULL) return;
485         }
486
487         if (theme == log_theme)
488                 return;
489
490         str = format_get_text_theme_charargs(log_theme, module, dest,
491                                              GPOINTER_TO_INT(formatnum), args);
492         skip_next_printtext = TRUE;
493
494         if (*str != '\0') {
495                 /* add the line start format */
496                 linestart = format_get_level_tag(log_theme, dest);
497                 tmp = str;
498                 str = format_add_linestart(tmp, linestart);
499                 g_free_not_null(linestart);
500                 g_free(tmp);
501
502                 /* strip colors from text, log it. */
503                 stripped = strip_codes(str);
504                 log_line(dest->window, dest->server, dest->target,
505                          dest->level, stripped);
506                 g_free(stripped);
507         }
508         g_free(str);
509
510 }
511
512 static int sig_autoremove(void)
513 {
514         SERVER_REC *server;
515         LOG_ITEM_REC *logitem;
516         GSList *tmp, *next;
517         time_t removetime;
518
519         removetime = time(NULL)-AUTOLOG_INACTIVITY_CLOSE;
520         for (tmp = logs; tmp != NULL; tmp = next) {
521                 LOG_REC *log = tmp->data;
522
523                 next = tmp->next;
524
525                 if (!log->temp || log->last > removetime || log->items == NULL)
526                         continue;
527
528                 /* Close only logs with private messages */
529                 logitem = log->items->data;
530                 if (logitem->servertag == NULL)
531                         continue;
532
533                 server = server_find_tag(logitem->servertag);
534                 if (logitem->type == LOG_ITEM_TARGET &&
535                     server != NULL && !server->ischannel(logitem->name))
536                         log_close(log);
537         }
538         return 1;
539 }
540
541 static void sig_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
542 {
543         LOG_REC *log;
544
545         log = logs_find_item(LOG_ITEM_TARGET, item->name,
546                              item->server == NULL ? NULL :
547                              item->server->tag, NULL);
548         if (log != NULL && log->temp)
549                 log_close(log);
550 }
551
552 static void sig_log_locked(LOG_REC *log)
553 {
554         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
555                     TXT_LOG_LOCKED, log->fname);
556 }
557
558 static void sig_log_create_failed(LOG_REC *log)
559 {
560         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
561                     TXT_LOG_CREATE_FAILED, log->fname, g_strerror(errno));
562 }
563
564 static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
565 {
566         char *str;
567         int msgs, filepos;
568
569         msgs = GPOINTER_TO_INT(pmsgs);
570         filepos = GPOINTER_TO_INT(pfilepos);
571
572         if (msgs == 0)
573                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->fname);
574         else {
575                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->fname, msgs);
576
577                 str = g_strdup_printf("\"%s\" %d", log->fname, filepos);
578                 signal_emit("command cat", 1, str);
579                 g_free(str);
580         }
581 }
582
583 static void sig_theme_destroyed(THEME_REC *theme)
584 {
585         if (theme == log_theme)
586                 log_theme = NULL;
587 }
588
589 static void read_settings(void)
590 {
591         int old_autolog = autolog_level;
592
593         autolog_path = settings_get_str("autolog_path");
594         autolog_level = !settings_get_bool("autolog") ? 0 :
595                 level2bits(settings_get_str("autolog_level"));
596
597         if (old_autolog && !autolog_level)
598                 autologs_close_all();
599
600         /* write to log files with different theme? */
601         if (log_theme_name != NULL)
602                 signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
603         log_theme_name = settings_get_str("log_theme");
604         if (*log_theme_name == '\0')
605                 log_theme_name = NULL;
606         else
607                 signal_add("print format", (SIGNAL_FUNC) sig_print_format);
608
609         log_theme = log_theme_name == NULL ? NULL :
610                 theme_load(log_theme_name);
611 }
612
613 void fe_log_init(void)
614 {
615         autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL);
616         skip_next_printtext = FALSE;
617
618         settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
619         settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
620         settings_add_bool("log", "autolog", FALSE);
621         settings_add_str("log", "log_theme", "");
622
623         autolog_level = 0;
624         log_theme_name = NULL;
625         read_settings();
626
627         command_bind("log", NULL, (SIGNAL_FUNC) cmd_log);
628         command_bind("log open", NULL, (SIGNAL_FUNC) cmd_log_open);
629         command_bind("log close", NULL, (SIGNAL_FUNC) cmd_log_close);
630         command_bind("log start", NULL, (SIGNAL_FUNC) cmd_log_start);
631         command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop);
632         command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log);
633         command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile);
634         signal_add_first("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped);
635         signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
636         signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
637         signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
638         signal_add("log locked", (SIGNAL_FUNC) sig_log_locked);
639         signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
640         signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
641         signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
642         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
643
644         command_set_options("log open", "noopen autoopen -targets window");
645 }
646
647 void fe_log_deinit(void)
648 {
649         g_source_remove(autoremove_tag);
650         if (log_theme_name != NULL)
651                 signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
652
653         command_unbind("log", (SIGNAL_FUNC) cmd_log);
654         command_unbind("log open", (SIGNAL_FUNC) cmd_log_open);
655         command_unbind("log close", (SIGNAL_FUNC) cmd_log_close);
656         command_unbind("log start", (SIGNAL_FUNC) cmd_log_start);
657         command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop);
658         command_unbind("window log", (SIGNAL_FUNC) cmd_window_log);
659         command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile);
660         signal_remove("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped);
661         signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
662         signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
663         signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
664         signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked);
665         signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
666         signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
667         signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
668         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
669 }